1.1 目标


了解内置函数式接口由来
了解常用内置函数式接口


1.2 内置函数式接口来由来


我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
image.png

1.3 常用内置函数式接口介绍


它们主要在 java.util.function 包中。下面是最常用的几个接口。

1. Supplier接口
image.png

  1. Consumer接口


image.png
3. Function接口
image.png
4. Predicate接口
image.png

1.4 Supplier接口


java.util.function.Supplier 接口,它意味着”供给” , 对应的Lambda表达式需要“对外提供”一个符合泛型类 型的对象数据。
image.png
供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口。

使用Lambda表达式返回数组元素最大值

使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用 java.lang.Integer 类。

代码示例:
image.png

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.Arrays;
  3. import java.util.function.Supplier;
  4. public class Demo02Supplier {
  5. // 使用Lambda表达式返回数组元素最大值
  6. public static void main(String[] args) {
  7. System.out.println("开始了");
  8. printMax(() -> {
  9. int[] arr = {11, 99, 88, 77, 22};
  10. // 先排序,最后就是最大的
  11. Arrays.sort(arr); // 升序排序
  12. return arr[arr.length - 1];
  13. });
  14. }
  15. public static void printMax(Supplier<Integer> supplier) {
  16. int max = supplier.get();
  17. System.out.println("max = " + max);
  18. }
  19. }

1.5 Consumer接口



java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛 型参数决定。
image.png
使用Lambda表达式将一个字符串转成大写和小写的字符串

Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。基本使用如:

image.png

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Consumer;
  3. public class Demo03Consumer {
  4. // 使用Lambda表达式将一个字符串转成大写的字符串
  5. public static void main(String[] args) {
  6. System.out.println("开始啦");
  7. printHello((String str) -> {
  8. System.out.println(str.toUpperCase());
  9. });
  10. }
  11. public static void printHello(Consumer<String> consumer) {
  12. System.out.println("aaa");
  13. consumer.accept("Hello World");
  14. }
  15. }

默认方法:andThen

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


image.png

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

要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合 的情况:
image.png

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Consumer;
  3. public class Demo04ConsumerAndThen {
  4. // 使用Lambda表达式先将一个字符串转成小写的字符串,再转成大写
  5. public static void main(String[] args) {
  6. System.out.println("开始啦");
  7. printHello((String str) -> {
  8. System.out.println(str.toLowerCase());
  9. }, (String str) -> {
  10. System.out.println(str.toUpperCase());
  11. });
  12. }
  13. public static void printHello(Consumer<String> c1, Consumer<String> c2) {
  14. System.out.println("aa");
  15. String str = "Hello World";
  16. // c1.accept(str);
  17. // c2.accept(str);
  18. c2.andThen(c1).accept(str);
  19. }
  20. }

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


1.6 Function接口


java.util.function.Function 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。有参数有返回值。

image.png
使用Lambda表达式将字符串转成数字

Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景 例如:将 String 类型转换为 Integer 类型。
image.png

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Function;
  3. public class Demo05Function {
  4. // 使用Lambda表达式将字符串转成数字
  5. public static void main(String[] args) {
  6. System.out.println("开始");
  7. getNumber((String str) -> {
  8. int i = Integer.parseInt(str);
  9. return i;
  10. });
  11. }
  12. public static void getNumber(Function<String, Integer> function) {
  13. System.out.println("aa");
  14. Integer num1 = function.apply("10");
  15. System.out.println("num1 = " + num1);
  16. }
  17. }

默认方法:andThen

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

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


image.png

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Function;
  3. public class Demo06FunctionAndThen {
  4. // 使用Lambda表达式将字符串转成数字, 第二个操作将这个数字乘以5
  5. public static void main(String[] args) {
  6. getNumber((String str) -> {
  7. return Integer.parseInt(str);
  8. }, (Integer i) -> {
  9. return i * 5;
  10. });
  11. }
  12. public static void getNumber(Function<String, Integer> f1, Function<Integer, Integer> f2) {
  13. System.out.println("aa");
  14. // Integer num = f1.apply("6");
  15. // Integer num2 = f2.apply(num);
  16. Integer num2 = f1.andThen(f2).apply("6");
  17. System.out.println("num2 = " + num2);
  18. }
  19. }

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

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

1.7 Predicate接口


有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。
image.png
使用Lambda判断一个人名如果超过3个字就认为是很长的名字

对test方法的参数T进行判断,返回boolean类型的结果。用于条件判断的场景:

image.png

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Predicate;
  3. public class Demo07Predicate {
  4. // 使用Lambda判断一个人名如果超过3个字就认为是很长的名字
  5. public static void main(String[] args) {
  6. System.out.println("开始啦");
  7. isLongName((String name) -> {
  8. return name.length() > 3;
  9. });
  10. }
  11. public static void isLongName(Predicate<String> predicate) {
  12. System.out.println("aa");
  13. boolean isLong = predicate.test("迪丽热巴");
  14. System.out.println("是否是长名字: " + isLong);
  15. }
  16. }

条件判断的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长。

默认方法:and

既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法 and 。其JDK源码为:
image.png
使用Lambda表达式判断一个字符串中即包含W,也包含H
使用Lambda表达式判断一个字符串中包含W或者包含H
使用Lambda表达式判断一个字符串中即不包含W


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

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Predicate;
  3. public class Demo08Predicate_And_Or_Negate {
  4. // 使用Lambda表达式判断一个字符串中即包含W,也包含H
  5. // 使用Lambda表达式判断一个字符串中包含W或者包含H
  6. // 使用Lambda表达式判断一个字符串中不包含W
  7. public static void main(String[] args) {
  8. test((String str) -> {
  9. // 判断是否包含W
  10. return str.contains("W");
  11. }, (String str) -> {
  12. // 判断是否包含H
  13. return str.contains("H");
  14. });
  15. }
  16. public static void test(Predicate<String> p1, Predicate<String> p2) {
  17. // String str = "Hello orld";
  18. // boolean b1 = p1.test(str);
  19. // boolean b2 = p2.test(str);
  20. // if (b1 && b2) {
  21. // System.out.println("即包含W,也包含H");
  22. // }
  23. // 使用Lambda表达式判断一个字符串中即包含W,也包含H
  24. String str = "Hello World";
  25. boolean b = p1.and(p2).test(str);
  26. if (b) {
  27. System.out.println("即包含W,也包含H");
  28. }
  29. }
  30. }

默认方法:or

使用Lambda表达式判断一个字符串中包含W或者包含H


与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:
image.png
如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:

  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Predicate;
  3. public class Demo08Predicate_And_Or_Negate {
  4. // 使用Lambda表达式判断一个字符串中即包含W,也包含H
  5. // 使用Lambda表达式判断一个字符串中包含W或者包含H
  6. // 使用Lambda表达式判断一个字符串中不包含W
  7. public static void main(String[] args) {
  8. test((String str) -> {
  9. // 判断是否包含W
  10. return str.contains("W");
  11. }, (String str) -> {
  12. // 判断是否包含H
  13. return str.contains("H");
  14. });
  15. }
  16. public static void test(Predicate<String> p1, Predicate<String> p2) {
  17. // String str = "Hello orld";
  18. // boolean b1 = p1.test(str);
  19. // boolean b2 = p2.test(str);
  20. // if (b1 && b2) {
  21. // System.out.println("即包含W,也包含H");
  22. // }
  23. // 使用Lambda表达式判断一个字符串中即包含W,也包含H
  24. String str = "Hello World";
  25. boolean b = p1.and(p2).test(str);
  26. if (b) {
  27. System.out.println("即包含W,也包含H");
  28. }
  29. // 使用Lambda表达式判断一个字符串中包含W或者包含H
  30. boolean b1 = p1.or(p2).test(str);
  31. if (b1) {
  32. System.out.println("包含W或者包含H");
  33. }
  34. }
  35. }


默认方法:negate

使用Lambda表达式判断一个字符串中即不包含W


“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法 negate 的JDK源代码为:


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


  1. package com.itheima.demo03functionalinterface;
  2. import java.util.function.Predicate;
  3. public class Demo08Predicate_And_Or_Negate {
  4. // 使用Lambda表达式判断一个字符串中即包含W,也包含H
  5. // 使用Lambda表达式判断一个字符串中包含W或者包含H
  6. // 使用Lambda表达式判断一个字符串中不包含W
  7. public static void main(String[] args) {
  8. test((String str) -> {
  9. // 判断是否包含W
  10. return str.contains("W");
  11. }, (String str) -> {
  12. // 判断是否包含H
  13. return str.contains("H");
  14. });
  15. }
  16. public static void test(Predicate<String> p1, Predicate<String> p2) {
  17. // String str = "Hello orld";
  18. // boolean b1 = p1.test(str);
  19. // boolean b2 = p2.test(str);
  20. // if (b1 && b2) {
  21. // System.out.println("即包含W,也包含H");
  22. // }
  23. // 使用Lambda表达式判断一个字符串中即包含W,也包含H
  24. String str = "Hello World";
  25. boolean b = p1.and(p2).test(str);
  26. if (b) {
  27. System.out.println("即包含W,也包含H");
  28. }
  29. // 使用Lambda表达式判断一个字符串中包含W或者包含H
  30. boolean b1 = p1.or(p2).test(str);
  31. if (b1) {
  32. System.out.println("包含W或者包含H");
  33. }
  34. // 使用Lambda表达式判断一个字符串中不包含W
  35. boolean b2 = p1.negate().test("Hello W");
  36. // negate相当于取反 !boolean
  37. if (b2) {
  38. System.out.println("不包含W");
  39. }
  40. }
  41. }

1.8 小结


  1. Supplier接口
    image.png
    1. Consumer接口


image.png
3. Function接口
image.png
4. Predicate接口
image.png