函数式接口

作者: 舞鹤Roc | 来源:发表于2022-09-19 10:20 被阅读0次
    函数式编程

    写在前面

    我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式。常见的编程范式还有逻辑式编程(Logic Programming),函数式编程(Functional Programming)。众所周知从Java 8开始引入了Lambda表达式,我们可以把它理解为一段可以传递的代码,可以写出更简洁、更灵活的代码。其基础内容不过多赘述,比如->、() 、{}符号含义,匿名函数,闭包,集合Stream操作等,本文重点探讨一下Java内置的函数式接口。

    @FunctionInterface函数式接口标注

    JDK1.8 API中包含了很多内置的函数式接口(例如Runnable、Callable、Comparator、FileFilter等),我们可以发现他们都用@FunctionalInterface注解来进行了标注。

    关于FunctionInterface注解:
    1、该注解只能标记在\color{#FF0000}{有且仅有一个抽象方法} 的接口上。
    1.1、JDK8接口中的静态方法和默认方法,都不算是抽象方法。
    1.2、接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
    2、\color{#FF0000}{该注解不是必须的},加上该注解能够更好地让编译器进行检查。

    Predicate 断言型接口 有入参|返回真假

    Stream中的filter使用的就是此函数式接口。其包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate),用于Predicate的复合,进行复杂判断,示例如下:

          // 示例1:初试
          Predicate<String> predicate = new Predicate<String>() {
                @Override
                public boolean test(String x) {
                    // 验证逻辑
                    return x.contains("predicate");
                }
            };
            // 可以简写为 Predicate<String> predicate = x -> x.contains("predicate");
            System.out.println(predicate.test("Test predicate"));
    
            // 示例2:复合
            Predicate<String> predicate1 = x -> x.contains("predicate");
            Predicate<String> predicate2 = x -> x.contains("Test");
            String param = "Test predicate";
            // 先写逻辑 后传参数
            System.out.println(predicate1.and(predicate2).test(param));
    

    Consumer 消费型接口 有入参|无返回值

    顾名思义,给定一个参数,对其进行任何操作(消费)处理。其包含了andThen方法,传入一个Consumer,返回的仍然是Consumer,以此实行链式调用,示例如下:

            // 示例1:初试
            Consumer<String> consumer = new Consumer<String>() {
                @Override
                public void accept(String s) {
                    System.out.println("执行逻辑 ------ 打印参数 : " + s);
                }
            };
            // 可简写为 Consumer<String> consumer = s -> System.out.println("执行逻辑 ------ 打印参数 : " + s);
            consumer.accept("Test Consumer");
    
            // 示例2:复合
            Consumer<String> consumer1 = s -> System.out.println("执行逻辑1 ------ 打印参数 : " + s);
            Consumer<String> consumer2 = s -> System.out.println("执行逻辑2 ------ 打印参数 : " + s);
            consumer1.andThen(consumer2).accept("Test Consumer");
    

    Supplier 供给型接口 无入参|有返回值

    同样顾名思义,执行后返回这个get方法的内容,我们可能会想没有参数,直接返回的为什么不直接定义一个常量呢?但是如果结果不是固定的,由具体get中的逻辑执行后得出,就不能使用常量了。
    比如我们 return "Test Supplier" + System.currentTimeMillis();

            // 示例:初试
            Supplier<String> supplier = new Supplier<String>() {
                @Override
                public String get() {
                    return "Test Supplier";
                }
            };
            // 可简写为 Supplier<String> supplier = () -> "Test supplier";
            System.out.println(supplier.get());
    

    Function<T,R>函数式接口 有入参|有返回值

    如果基于之上的Consumer和Supplier,我们去理解Function,就太简单了。这个就跟我们初中学的函数一样了,Function同时提供了compose(之前执行)和andThen(之后执行),可以进行复合操作,示例如下:

           // 示例1:初试
           Function<String, Integer> function = new Function<String, Integer>() {
               @Override
               public Integer apply(String s) {
                   return s.length();
               }
           };
           // 可简写为 Function<Integer, String> function = x -> "This is Integer:" + x;
           System.out.println(function.apply("Test Function"));
    
            // 示例2:复合
            Function<String, Integer> function1 = s -> s.length();
            Function<String, String> function2 = s -> s.replaceAll("Function", "");
            System.out.println(function1.compose(function2).apply("Test Function"));
    

    Consumer、Predicate、Function复合 怎么优雅怎么来

    上面是单独的复合,当然他们也可以进行任何组合的复合,这样写的代码就更优雅了。

            // Supplier、Consumer、Function复合
            Supplier<String> supplier = () -> "Test Function & Supplier";
            Consumer<String> consumer = s -> System.out.println("执行逻辑 ------ 打印参数 : " + s);
    
            Function<String, Integer> function1 = s -> {
                // Function执行添加Consumer打印参数逻辑
                consumer.accept(s);
                return s.length();
            };
            Function<String, String> function2 = s -> s.replaceAll("Function", "");
            // 将Function的参数改为Supplier
            System.out.println(function1.compose(function2).apply(supplier.get()));
    

    集合Stream流中的函数式编程

    .filter()里面需要传入Predicate,根据test结果返回Optional。

    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }
    

    .forEach()里面需要传入Consumer,单纯的执行一段业务逻辑。

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
         action.accept(t);
       }
    }
    

    .map()里需要传入Function,支持数据的自定义转换。

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
      Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    

    在你完全理解函数式接口之后,很多源码的封装套路就一目了然了。

    作者:舞鹤Roc
    链接:https://www.jianshu.com/p/a7c2e4130349
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

        本文标题:函数式接口

        本文链接:https://www.haomeiwen.com/subject/bynmortx.html