1. 概述

1.1背景

在日常Java编码中,函数式编程使用的频率是非常高的,但是回想一下,自己虽然能很好的使用,但是却没能系统的了解过函数式编程,所以以下为最近学习的一个总结。

1.2 概念

函数式接口在Java中是指:有且仅有一个抽象方法的接口函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

2. 函数式接口介绍&基本使用

2.1 格式

  1. 修饰符 interface 接口名称 {
  2. public abstract 返回值 方法名称(参数列表)
  3. // 其他方式
  4. }
  5. // public abstract 可以不写 编译器自动加上
  6. 修饰符 interface 接口名称 {
  7. 返回值 方法名称(参数列表)
  8. // 其他方式
  9. }

以最常用的Function为例:

  1. @FunctionalInterface
  2. public interface Function<T, R> {
  3. R apply(T t);
  4. default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
  5. Objects.requireNonNull(before);
  6. return (V v) -> apply(before.apply(v));
  7. }
  8. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
  9. Objects.requireNonNull(after);
  10. return (T t) -> after.apply(apply(t));
  11. }
  12. static <T> Function<T, T> identity() {
  13. return t -> t;
  14. }
  15. }

2.2 @FunctionalInterface注解

  1. // 标明为函数式接口
  2. @FunctionalInterface
  3. public interface MyFunction {
  4. // 抽象方法
  5. void method();
  6. }

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。(该接口是一个标记接口)

2.3 自定义函数式接口使用

  1. public static void executeMyFunction(MyFunction function) {
  2. function.method();
  3. }
  4. public static void main(String[] args) {
  5. executeMyFunction(()-> System.out.println("Hello World"));
  6. }

2.4 Java自带函数式接口

Java 函数式接口详解 - 图1
常用函数式接口

  1. Supplier 你要作为一个供应者,自己生产数据
  2. Consumer 你要作为一个消费者,利用已经准备数据
  3. Function 输入一个或者两个不同或者相同的值转为另一个值
  4. Predicate 输入一个或者两个不同或者相同的值总是输出boolean
  5. UnaryOperator 输入一个值转换为相同值输出
  6. BinaryOperator 输入两个相同类型的值 转为相同类型的值输出

2.4 主要语法

  1. () -> 代表了 lambda的一个表达式
  2. 单行代码无需写return (无论函数式接口有没有返回值),花括号
  3. 多行代码必须写花括号,有返回值的一定要写返回值
  4. 单行代码且有参数的情况下可以不写 () 如 s->System.out.println(s)
  5. (T t)中的参数类型可写可不写

3. 常用函数式接口

3.1 Supplier接口(供应接口)

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

    1. private static String supplierFunction(Supplier<String> stringSupplier){
    2. return stringSupplier.get();
    3. }
    4. public static void main(String[] args) {
    5. System.out.println(supplierFunction(()->"Hello World"));
    6. }

    3.2 Consumer接口

  • 接口名:java.util.function.Consumer

  • 抽象方法:void accept(T t)
  • 作用:接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定

  • 默认方法:andThen

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

    1. default Consumer<T> andThen(Consumer<? super T> after) {
    2. //1: 返回值为Consumer 那么需要 ()-> 表示函数式接口
    3. //2: accept(t);为生产一个数据供应给 (T t)中的t
    4. //3: after.accept(t);为利用这个t再次生成新的函数式接口 实现类始于builder的设计模式
    5. Objects.requireNonNull(after);
    6. return (T t) -> { accept(t); after.accept(t); };
    7. }

    3.3 Predicate接口

  • 接口名:java.util.function.Predicate

  • 抽象方法:_boolean _test(T t)
  • 作用:需要对某种类型的数据进行判断,从而得到一个boolean值结果

  • 默认方法:

    • Predicate and(Predicate<? _super _T> other)
    • _default _Predicate or(Predicate<? _super _T> other)
    • _default _Predicate negate() (取反)
  • 作用:既然是条件判断,就会存在与、或、非三种常见的逻辑关系。

  • 静态方法:static Predicate isEqual(Object targetRef)

  • 作用:返回值为Predicate,返回一个是否与targetRef相等的函数表达式

3.4 Function接口

  • 接口名:java.util.function.Function
  • 抽象方法:R apply(T t)
  • 作用:根据一个类型的数据得到另一个类型的数据

  • 默认方法:

    • default Function compose(Function<? _super _V, ? _extends _T> before)
    • default Function andThen(Function<? _super _R, ? _extends _V> after)
  • 作用:与另一个Function进行结合,一个方法为before,另一个为after
    1. default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    2. Objects.requireNonNull(before);
    3. return (V v) -> apply(before.apply(v));
    4. }
    5. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    6. Objects.requireNonNull(after);
    7. return (T t) -> after.apply(apply(t));
    8. }

参考