函数式接口

函数式编程

写在前面

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

@FunctionInterface函数式接口标注

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

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

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

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

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

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

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

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

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

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

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

Function函数式接口 有入参|有返回值

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

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

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

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

        // Supplier、Consumer、Function复合
        Supplier supplier = () -> "Test Function & Supplier";
        Consumer consumer = s -> System.out.println("执行逻辑 ------ 打印参数 : " + s);

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

集合Stream流中的函数式编程

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

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

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

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

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

public Optional map(Function mapper) {
  Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

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

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

你可能感兴趣的:(函数式接口)