Java基础:JDK8 四大核心函数式接口详解与应用

函数式接口 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口(default 修饰),这又关联了 Java 8 的另一个新特性,允许在接口中定义默认方法(default 修饰)

函数式接口是行为的抽象,是数据转换。其最直接表现是将函数(行为)作为数据传递进方式中。

函数式编程的一大好处,是可以用更精练的代码来表达常用数据处理。函数接口能够轻易地实现模板方法模式,只要将不确定的业务逻辑抽象成函数接口,然后传入不同的 Lambda 表达式即可。

JDK 8 新特性中的 Lambda 表达式是 Java 8 发布的最重要的特性,Lambda 表达式允许把函数作为一个方法的参数传递 (函数作为参数传递进方法中),但是只能用在重写函数式接口的抽象方法上。

与 Lambda 表达式功能一起发布的 函数式接口 是其重要组成部分。Lambda 表达式和方法引用就是针对 函数式接口的使用(即只能用在函数式接口上),因为 Lambda 表达式没有方法名,所以针对的就是接口中的抽象方法,则要求函数式接口中有且只有一个抽象方法,否则就不知道 Lambda 重写的是哪个抽象方法。

Lambda 表达式只能用在重写函数式接口的抽象方法上,因为函数式接口只有一个抽象方法,编译器会认为 Lambda 表达式写的方法就是接口的唯一的抽象方法。

JDK 8 以前的接口中的所有方法必须都是抽象的,不能有非抽象方法,其实现类必须实现其所有方法,而 JDK 在更新过程中可能对原有的接口进行扩展,若在原有接口中加入抽象方法,那实现了该接口的类要全部重写新加入的方法,为了解决更新接口同时又兼容以前版本的程序,就支持在接口中增加 default 方法,变相的让 Java 支持多继承,因为可以实现多个接口。

Java 中的 Lambda 表达式的功能更多应用在流数据处理中(负责迭代数据的集合)。

FunctionalInterface

JDK 8 新增了 @FunctionalInterface 注解,用于标识接口类型声明为函数式接口,从概念上讲,函数式接口只有一个抽象方法。可以通过 java.lang.reflect.Method#isDefault() 反射操作来判断是否为默认方法。

注意:函数式接口的实现可以使用 Lambda 表达式,方法引用或函数引用创建。

@FunctionalInterface 注解只能作用在接口类型上,不能在注解类型,枚举类型 或 类上使用且。作用的目标接口必须满足函数式接口的要求。

@FunctionalInterface 注解用于显式标注一个函数式接口,该注解主要用于编译级错误检查,使用了该注解后,如果自定义接口不满足函数式接口的要求,会报错。并不是所有函数式接口都必须使用该注解进行标识,编译器会把满足函数接口定义的任何接口视为函数接口,而不管接口声明中是否存在 @FunctionalInterface 注解。

4个核心函数式接口

JDK 8 在 java.util.function 包中定义了一些基础的函数式接口,其中有四个可认为是核心的函数式接口:

函数式接口 类型别名 描述 备注
Function<T,R> 函数型 数据转换器,接收一个 T 类型的对象,
返回一个 R 类型的对象,单参数单返回值的行为接口;
提供了 apply, compose, andThen, identity 方法
Supplier<T> 供给型 数据提供器,可以提供 T 类型对象;
无参的构造器,提供了 get 方法
Consumer<T> 消费型 数据消费器, 接收一个 T 类型的对象,无返回值;
通常用于设置 T 对象的值;
单参数无返回值的行为接口;提供了 accept, andThen 方法
Predicate<T> 断定型 条件测试器,接收一个 T 类型的对象,返回布尔值;
通常用于传递条件函数; 单参数布尔值的条件性接口;
提供了 test (条件测试) , and-or- negate(与或非) 方法;
其中, compose, andThen, and, or, negate 用来组合函数接口
而得到更强大的函数接口。

其它的函数接口都是通过这四个扩展而来:

  • 在参数个数上扩展: 比如接收双参数的,有 Bi 前缀, 比如 BiConsumer<T, U>, BiFunction<T, U, R>
  • 在类型上扩展: 比如接收原子类型参数的,有 [Int|Double|Long|Function|Consumer|Supplier|Predicate]。
  • 特殊常用的变形: 比如 BinaryOperator , 是同类型的双参数 BiFunction<T, T, T> ,二元操作符 。UnaryOperator 是 Function<T, T> 一元操作符。

函数式接口可以接收的值类型有:

  • 类/对象的静态方法引用、实例方法引用。引用符号为双冒号 ::
  • 类的构造器引用,比如 Class::new
  • Lambda 表达式。

Consumer接口

接口定义

对入参做一些操作,没有返回值(相当于内部消费掉传入的参数)。

在 Stream 里,主要用于内部 ForEach,内部迭代,对传入的参数做一系列的业务操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 表示接受单个输入参数但不返回结果的操作
* 与大多数其他函数接口不同,Consumer 需要通过额外的作用来操作
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {

/**
* 单一抽象方法
* 对给定参数执行此操作。
*/
void accept(T t);

/**
* 用于复合操作的默认方法
* 返回一个组合的{@code Consumer},按顺序执行此操作,然后执行{@code after}操作。
* 如果执行任一操作时引发异常,则将异常中继到组合操作的调用方。
* 如果执行此操作引发异常,则不会执行{@code after}操作。
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

使用示例

void accept(T t):

Lambda 表达式或方法引用相当于重写内部 accept(T t) 方法。

  1. 示例一:consumerTest1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Test
    public void consumerTest1() {
    List<String> list = Arrays.asList("Tom", "Kitty", "Rose", "Alex", "Andy");

    // 匿名内部类(重写内部 accept 方法, 默认调用单一的抽象方法)
    list.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
    System.out.println(s);
    }
    });
    System.out.println("----------------------");

    // Lambda 表达式(重写内部 accept 方法, 默认调用单一的抽象方法)
    list.forEach(s -> System.out.println(s));
    System.out.println("----------------------");

    // 方法引用(重写内部 accept 方法, 默认调用单一的抽象方法)
    list.forEach(System.out::println);
    }

    结果输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Tom
    Kitty
    Rose
    Alex
    Andy
    ----------------------
    Tom
    Kitty
    Rose
    Alex
    Andy
    ----------------------
    Tom
    Kitty
    Rose
    Alex
    Andy
  2. 示例二:consumerTest2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    * 1.消费型 Consumer<T> void accept(T t)
    */
    @Test
    public void consumerTest2() {
    // 匿名方法
    Consumer<String> c1 = new Consumer<String>() {
    @Override
    public void accept(String str) {
    System.out.println(str);
    }
    };

    // Lambda 表达式
    Consumer<String> c2 = str -> System.out.println(str);

    // 方法引用
    Consumer<String> c3 = System.out::println;
    c3.accept("Hello World");
    }

    输出结果:

    1
    2
    3
    4
    Hello World 1

    Hello World 2
    Hello World 3
  3. 示例三:consumerTest3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public void happy(double money, Consumer<Double> con) {
    consumer.accept(money);
    }

    @Test
    public void consumerTest3() {
    // Lambda
    this.happy(200D, s -> System.out.println(s));

    // 方法引用
    this.happy(200D, System.out::println);

    // 匿名方法
    this.happy(200D, new Consumer<Double>() {
    @Override
    public void accept(Double money) {
    System.out.println(money);
    }
    });
    }

    输出结果:

    1
    2
    3
    200.0
    200.0
    200.0

andThen(Consumer<? super T> after)

  1. 示例一:consumerAndThen1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Test
    public void consumerAndThen1() {
    String[] strArr = {"Tom:boy", "Kitty:girl", "Rose:girl"};
    // 标准Lambda可以使用 lambda表达式替换
    printInfo(strArr, (message) -> {
    System.out.print("Name:" + message.split(":")[0] + "");
    }, (message) -> {
    System.out.println("Gender:" + message.split(":")[1] + "");
    });
    // Lambda 表达式
    printInfo(strArr, (message) -> System.out.print("Name:" + message.split(":")[0] + ""),
    (message) -> System.out.println("Gender:" + message.split(":")[1] + ""));
    }

    // 传入多个 Consumer,指定执行顺序
    public void printInfo(String[] strArr, Consumer<String> con1, Consumer<String> con2) {
    for (int i = 0; i < strArr.length; i++) {
    con1.andThen(con2).accept(strArr[i]);
    }
    }

    输出结果:

    1
    2
    3
    4
    5
    6
    Name:TomGender:boy
    Name:KittyGender:girl
    Name:RoseGender:girl
    Name:TomGender:boy
    Name:KittyGender:girl
    Name:RoseGender:girl
  2. 示例二:consumerAndThen2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Test
    public void consumerAndThen2() {
    List<Integer> list = Arrays.asList(1, 3, 2, 6, 9, 5);

    Consumer<Integer> consumer = x -> System.out.print(x);
    // 组合 Consumer
    Consumer<Integer> consumerAndThen = consumer.andThen(i -> {
    System.out.println(", print:" + i);
    });

    list.forEach(x -> consumerAndThen.accept(x));
    System.out.println("----------------------");

    list.forEach(consumerAndThen::accept);
    System.out.println("----------------------");

    // 函数调用,调用默认的抽象方法accept
    list.forEach(consumerAndThen);
    System.out.println("----------------------");

    for (Integer integer : list) {
    consumerAndThen.accept(integer);
    }
    }

    输出结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    1, print:1
    3, print:3
    2, print:2
    6, print:6
    9, print:9
    5, print:5
    ----------------------
    1, print:1
    3, print:3
    2, print:2
    6, print:6
    9, print:9
    5, print:5
    ----------------------
    1, print:1
    3, print:3
    2, print:2
    6, print:6
    9, print:9
    5, print:5
    ----------------------
    1, print:1
    3, print:3
    2, print:2
    6, print:6
    9, print:9
    5, print:5

Supplier接口

Consumer 函数式接口相当于消费者,Supplier 接口就相当于生产者了,只有一个 get() 抽象方法返回结果。

接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 表示结果的提供者
* 不要求每次调用 supplier 时都返回一个新的或不同的结果
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {

/**
* 获取结果
*/
T get();
}

使用示例

T get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SupplierTest {

@Test
public void supplierTest1() {
Supplier<String> supplier = String::new;
String string = supplier.get();
}

@Test
public void supplierTest2() {
Supplier<User> supplier = User::new;
User user = supplier.get();
}

@Test
public void supplierTest3() {
Supplier<String> supplier = () -> "Hello World";
System.out.println(supplier.get());
}


public List<Integer> getNumList(int num, Supplier<Integer> supplier) {
List<Integer> list = new ArrayList<>();

for (int i = 0; i < num; i++) {
Integer n = supplier.get();
list.add(n);
}
return list;
}

@Test
public void supplierTest4() {
List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));

for (Integer num : numList) {
System.out.println(num);
}
}

}

Function接口

Function 接收参数,然后运算转换为结果返回。

接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* 表示接受一个参数并生成结果的函数
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {

/**
* 将此函数应用于给定参数
*/
R apply(T t);

/**
* 返回一个组合函数, 先做入参 before 函数的apply操作, 然后做当前接口的apply操作
* 如果任何一个函数的求值引发异常, 它将被中继到组合函数的调用者
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

/**
* 返回一个组合函数, 先做本接口数的apply操作, 然后做入参 after 函数的apply操作
* 如果任何一个函数的求值引发异常, 它将被中继到组合函数的调用者。
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

/**
* 静态方法,返回一个始终返回其输入参数的函数
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}

使用示例

R apply(T t)

  1. 示例一

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    @Test
    public void functionTest1() {

    // 字符串
    Function<Integer, Integer> fun1 = x -> x * 2;
    System.out.println(fun1.apply(4));// 8

    Function<Integer, String> fun2 = x -> x * 2 + "aa";
    System.out.println(fun2.apply(4));//8dd

    // 字符串对象
    Function<String, String> strFun3 = (str) -> new String(str);
    System.out.println(strFun3.apply("bb"));//bb

    Function<String, String> strFun4 = String::new;
    System.out.println(strFun4.apply("cc"));//cc

    // 对象
    Function<String, User> objFun5 = (str) -> new User(str);
    System.out.println(objFun5.apply("dd").getName());//dd

    Function<String, User> objFun6 = User::new;
    System.out.println(objFun6.apply("ee").getName());//ee
    }

    @Test
    public void functionTest2() {
    // 1.函数型 Function<T,R> R apply(T t)
    Function<String, Integer> function2 = s -> s.length();
    Function<String, Integer> function1 = String::length;
    System.out.println(function1.apply("hello world"));
    System.out.println(function2.apply("hello world"));
    //结果:11
    }

    // Function<T, R> 函数型接口
    // 需求:用于处理字符串
    public String strHandler(String str, Function<String, String> fun) {
    return fun.apply(str);
    }

    @Test
    public void functionTest3() {
    String newStr2 = strHandler("\t\t\t Hello World", (str) -> str.trim());
    String newStr1 = strHandler("\t\t\t Hello World", String::trim);
    System.out.println(newStr1);
    System.out.println(newStr2);

    String subStr = strHandler("\t\t\t Hello World", (str) -> str.trim().substring(0, 4));
    System.out.println(subStr);
    //Hello World
    //Hello World
    //Hell
    }
  2. 示例二

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class FunctionTest1 {

    public static void main(String[] args) {
    FunctionTest fun = new FunctionTest();
    // 传递行为,而不是传递值
    System.out.println(fun.comput(1, result -> 2 * num));
    System.out.println(fun.comput(2, result -> 5 + num));
    System.out.println(fun.comput(3, Integer::intValue));
    System.out.println(fun.convert(4, result -> num + "helloworld"));
    }

    public int comput(int num, Function<Integer, Integer> function) {
    //apply ,传递的是行为
    int result = function.apply(num);
    return result;
    }

    public String convert(int num, Function<Integer, String> function) {
    return function.apply(num);
    }

    // 对于之前只传递值的写法,几种行为就要定义几种写法。 现在可以使用上面的方式去 传递行为
    public int method1(int num) {
    return num + 1;
    }

    public int method2(int num) {
    return num * 5;
    }

    public int method3(int num) {
    return num * num;
    }
    }

Predicate接口

接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 一个参数的布尔值函数。
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {

/**
* 根据给定参数计算布尔表达式
*/
boolean test(T t);

/**
* 返回一个组合的布尔表达式, 以表示 this 布尔表达式 与 other 布尔表达式的短路逻辑与(&&)
* 如果 this 为 false, 则不会评估
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

/**
* 返回取反的布尔表达式
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}

/**
* 返回一个组合的布尔表达式, 以表示 this 布尔表达式 与 other 布尔表达式的短路逻辑或(||)
* 如果 this 为 true, 则不会评估
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}

/**
* 返回给定的两个参数是否相等的布尔表达式
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}

使用示例

boolean test(T t)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class PredicatePreTest {

@Test
public void test1() {
//断定型 Predicate<T> boolean test(T t)
Predicate<Integer> predicate = num -> num >= 100;
System.out.println(predicate.test(99));
//false
}

@Test
public void test2() {
List<String> list = Arrays.asList("Tom", "Kitty", "Rose", "Andy");
List<String> strList = filterStr(list, (s) -> s.length() > 3);

for (String str : strList) {
System.out.println(str);
//Kitty
//Rose
//Andy
}
}

//需求:将满足条件的字符串,放入集合中去
public List<String> filterStr(List<String> list, Predicate<String> pre) {
List<String> strList = new ArrayList<>();
for (String str : list) {
if (pre.test(str)) {
strList.add(str);
}
}
return strList;
}
}

相关参考

  1. Java 8 函数式编程探秘 ( 上 )
  2. Java 8 函数式编程探秘 ( 下 )
  3. JAVA8 Consumer接口
  4. JDK 8 新特性一览
  5. Java 8 新特性
  6. JDK8-十大新特性
  7. Java 8 函数式接口
  8. JDK8函数式接口
  9. Java 8的核心新特性:Lambda(匿名函数)、流、默认方法
  10. JAVA8学习-深入浅出函数式接口FunctionInterface
  11. JDK8新特性之函数式接口

Java基础:JDK8 四大核心函数式接口详解与应用

http://blog.gxitsky.com/2020/06/18/Java-jdk-15-function-interface/

作者

光星

发布于

2020-06-18

更新于

2022-06-17

许可协议

评论