A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 森111 中级黑马   /  2018-12-2 17:02  /  585 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

函数式接口

概念:有且仅有一个抽象方法的接口

格式:

修饰符 interface 接口名称 {

    public abstract 返回值类型 方法名称(可选参数信息);

    // 其他非抽象方法内容

}

@FunctionalInterface注解

@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:

强制检查该接口是否确实有且仅有一个抽象方法,不是将会报错

@FunctionalInterface

public interface MyFunctionalInterface {

        void myMethod();

}

自定义函数式接口

public class Demo09FunctionalInterface {       

        // 使用自定义的函数式接口作为方法参数

        private static void doSomething(MyFunctionalInterface inter) {

                inter.myMethod(); // 调用自定义的函数式接口方法

        }


        public static void main(String[] args) {

                // 调用使用函数式接口的方法

                doSomething(() -> System.out.println("Lambda执行啦!"));

        }

}


函数式编程

Lambda的延迟执行

有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。

使用Lambda作为参数和返回值


public class Demo04Runnable {

    private static void startThread(Runnable task) {

              new Thread(task).start();

    }


    public static void main(String[] args) {

              startThread(()-> System.out.println("线程任务执行!")); // 参数为lambda表达式

    }

}


当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调该方法获取:

import java.util.Arrays;

import java.util.Comparator;


public class Demo06Comparator {

    private static Comparator<String> newComparator() {

              return (a, b) -> b.length() - a.length();

    }


    public static void main(String[] args) {

        String[] array = { "abc", "ab", "abcd" };

        System.out.println(Arrays.toString(array));

        Arrays.sort(array, newComparator());

        System.out.println(Arrays.toString(array));

    }

}


常用函数式接口

Supplier接口

java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

import java.util.function.Supplier;


public class Demo08Supplier {

    private static String getString(Supplier<String> function) {

              return function.get();

    }


    public static void main(String[] args) {

        String msgA = "Hello";

        String msgB = "World";

        System.out.println(getString(() -> msgA + msgB));

    }

}


练习:求数组元素最大值

使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值

接口的泛型:Integer类。

public class Demo02Test {

    //定一个方法,方法的参数传递Supplier,泛型使用Integer

    public static int getMax(Supplier<Integer> sup){

        return sup.get();

    }


    public static void main(String[] args) {

        int arr[] = {2,3,4,52,333,23};


        //调用getMax方法,参数传递Lambda

        int maxNum = getMax(()->{

           //计算数组的最大值

           int max = arr[0];

           for(int i : arr){

               if(i>max){

                   max = i;

               }

           }

           return max;

        });

        System.out.println(maxNum);

    }

}


Consumer接口

java.util.function.Consumer<T>接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

抽象方法:accept

import java.util.function.Consumer;


public class Demo09Consumer {

    private static void consumeString(Consumer<String> function) {

              function.accept("Hello");

    }


    public static void main(String[] args) {

        consumeString(s -> System.out.println(s));

    }

}

默认方法:andThen

如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen

default Consumer<T> andThen(Consumer<? super T> after) {

    Objects.requireNonNull(after);

    return (T t) -> { accept(t); after.accept(t); };

}

备注:java.util.ObjectsrequireNonNull静态方法将会在参数为null时主动抛出NullPointerException异常。这省去了重复编写if语句和抛出空指针异常的麻烦。

andThen:

import java.util.function.Consumer;


public class Demo10ConsumerAndThen {

    private static void consumeString(Consumer<String> one, Consumer<String> two) {

              one.andThen(two).accept("Hello");

    }


    public static void main(String[] args) {

        consumeString(

            s -> System.out.println(s.toUpperCase()),

            s -> System.out.println(s.toLowerCase()));

    }

}

运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

练习:格式化打印信息

下面的字符串数组当中存有多条信息,请按照格式“姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别的动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序“拼接”到一起。

public static void main(String[] args) {

          String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };

}

解答

import java.util.function.Consumer;


public class DemoConsumer {

    public static void main(String[] args) {

        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };

        printInfo(s -> System.out.print("姓名:" + s.split(",")[0]),

                  s -> System.out.println("。性别:" + s.split(",")[1] + "。"),

                  array);

    }


    private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {

        for (String info : array) {

            one.andThen(two).accept(info); // 姓名:迪丽热巴。性别:女。

        }

    }

}

Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate<T>接口。

抽象方法:test

Predicate接口中包含一个抽象方法:boolean test(T t)。用于条件判断的场景:

import java.util.function.Predicate;


public class Demo15PredicateTest {

    private static void method(Predicate<String> predicate) {

        boolean veryLong = predicate.test("HelloWorld");

        System.out.println("字符串很长吗:" + veryLong);

    }


    public static void main(String[] args) {

        method(s -> s.length() > 5);

    }

}

默认方法:and

既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法and。其JDK源码为:

default Predicate<T> and(Predicate<? super T> other) {

    Objects.requireNonNull(other);

    return (t) -> test(t) && other.test(t);

}


如果要判断一个字符串既要包含大写“H”,又要包含大写“W”,那么:

import java.util.function.Predicate;


public class Demo16PredicateAnd {

    private static void method(Predicate<String> one, Predicate<String> two) {

        boolean isValid = one.and(two).test("Helloworld");

        System.out.println("字符串符合要求吗:" + isValid);

    }


    public static void main(String[] args) {

        method(s -> s.contains("H"), s -> s.contains("W"));

    }

}


默认方法:or

and的“与”类似,默认方法or实现逻辑关系中的“”。JDK源码为:

default Predicate<T> or(Predicate<? super T> other) {

    Objects.requireNonNull(other);

    return (t) -> test(t) || other.test(t);

}

如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:

import java.util.function.Predicate;


public class Demo16PredicateAnd {

    private static void method(Predicate<String> one, Predicate<String> two) {

        boolean isValid = one.or(two).test("Helloworld");

        System.out.println("字符串符合要求吗:" + isValid);

    }


    public static void main(String[] args) {

        method(s -> s.contains("H"), s -> s.contains("W"));

    }

}


默认方法:negate

默认方法negate的JDK源代码为:

default Predicate<T> negate() {

    return (t) -> !test(t);

}

从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在test方法调用之前调用negate方法,正如andor方法一样:

import java.util.function.Predicate;


public class Demo17PredicateNegate {

    private static void method(Predicate<String> predicate) {

        boolean veryLong = predicate.negate().test("HelloWorld");

        System.out.println("字符串很长吗:" + veryLong);

    }


    public static void main(String[] args) {

              method(s -> s.length() < 5);

    }

}


练习:集合信息筛选

数组当中有多条“姓名+性别”的信息如下,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,需要同时满足两个条件:

1.必须为女生;
2.姓名为4个字。

public class DemoPredicate {

    public static void main(String[] args) {

              String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };

    }

}

解答

import java.util.ArrayList;

import java.util.List;

import java.util.function.Predicate;


public class DemoPredicate {

    public static void main(String[] args) {

        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };

        List<String> list = filter(array,

                                   s -> "女".equals(s.split(",")[1]),

                                   s -> s.split(",")[0].length() == 4);

        System.out.println(list);

    }


    private static List<String> filter(String[] array, Predicate<String> one,

                                       Predicate<String> two) {

        List<String> list = new ArrayList<>();

        for (String info : array) {

            if (one.and(two).test(info)) {

                list.add(info);

            }

        }

        return list;

    }

}

Function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

抽象方法:apply

Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。

使用的场景例如:将String类型转换为Integer类型

import java.util.function.Function;


public class Demo11FunctionApply {

    private static void method(Function<String, Integer> function) {

        int num = function.apply("10");

        System.out.println(num + 20);

    }


    public static void main(String[] args) {

        method(s -> Integer.parseInt(s));

    }

}

默认方法:andThen

Function接口中有一个默认的andThen方法,用来进行组合操作。JDK源代码如:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {

    Objects.requireNonNull(after);

    return (T t) -> after.apply(apply(t));

}

该方法同样用于“先做什么,再做什么”的场景,和Consumer中的andThen差不多:

import java.util.function.Function;


public class Demo12FunctionAndThen {

    private static void method(Function<String, Integer> one, Function<Integer, Integer> two) {

        int num = one.andThen(two).apply("10");

        System.out.println(num + 20);

    }


    public static void main(String[] args) {

        method(str->Integer.parseInt(str)+10, i -> i *= 10);

    }

}

第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过andThen按照前后顺序组合到了一起。

请注意,Function的前置条件泛型和后置条件泛型可以相同。

练习:自定义函数模型拼接


请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:

        String str = "赵丽颖,20";

1.将字符串截取数字年龄部分,得到字符串;
2.将上一步的字符串转换成为int类型的数字;
3.将上一步的int数字累加100,得到结果int数字。
解答

import java.util.function.Function;


public class DemoFunction {

    public static void main(String[] args) {

        String str = "赵丽颖,20";

        int age = getAgeNum(str, s -> s.split(",")[1],

                            s ->Integer.parseInt(s),

                            n -> n += 100);

        System.out.println(age);

    }


    private static int getAgeNum(String str, Function<String, String> one,

                                 Function<String, Integer> two,

                                 Function<Integer, Integer> three) {

        return one.andThen(two).andThen(three).apply(str);

    }

}





0 个回复

您需要登录后才可以回帖 登录 | 加入黑马