1.1 目标
1.2 内置函数式接口来由来
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽
象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
1.3 常用内置函数式接口介绍
它们主要在 java.util.function 包中。下面是最常用的几个接口。
1. Supplier接口
- Consumer接口
1.4 Supplier接口
java.util.function.Supplier 接口,它意味着”供给” , 对应的Lambda表达式需要“对外提供”一个符合泛型类 型的对象数据。
供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口。
使用Lambda表达式返回数组元素最大值
使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用
java.lang.Integer 类。
代码示例:
package com.itheima.demo03functionalinterface;
import java.util.Arrays;
import java.util.function.Supplier;
public class Demo02Supplier {
// 使用Lambda表达式返回数组元素最大值
public static void main(String[] args) {
System.out.println("开始了");
printMax(() -> {
int[] arr = {11, 99, 88, 77, 22};
// 先排序,最后就是最大的
Arrays.sort(arr); // 升序排序
return arr[arr.length - 1];
});
}
public static void printMax(Supplier<Integer> supplier) {
int max = supplier.get();
System.out.println("max = " + max);
}
}
1.5 Consumer接口
java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛 型参数决定。
使用Lambda表达式将一个字符串转成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。基本使用如:
package com.itheima.demo03functionalinterface;
import java.util.function.Consumer;
public class Demo03Consumer {
// 使用Lambda表达式将一个字符串转成大写的字符串
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System.out.println(str.toUpperCase());
});
}
public static void printHello(Consumer<String> consumer) {
System.out.println("aaa");
consumer.accept("Hello World");
}
}
默认方法:andThen
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操
作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代
码:
备注: java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出
NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。
要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合
的情况:
package com.itheima.demo03functionalinterface;
import java.util.function.Consumer;
public class Demo04ConsumerAndThen {
// 使用Lambda表达式先将一个字符串转成小写的字符串,再转成大写
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System.out.println(str.toLowerCase());
}, (String str) -> {
System.out.println(str.toUpperCase());
});
}
public static void printHello(Consumer<String> c1, Consumer<String> c2) {
System.out.println("aa");
String str = "Hello World";
// c1.accept(str);
// c2.accept(str);
c2.andThen(c1).accept(str);
}
}
运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组 合。
1.6 Function接口
java.util.function.Function 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。有参数有返回值。
使用Lambda表达式将字符串转成数字
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景
例如:将 String 类型转换为 Integer 类型。
package com.itheima.demo03functionalinterface;
import java.util.function.Function;
public class Demo05Function {
// 使用Lambda表达式将字符串转成数字
public static void main(String[] args) {
System.out.println("开始");
getNumber((String str) -> {
int i = Integer.parseInt(str);
return i;
});
}
public static void getNumber(Function<String, Integer> function) {
System.out.println("aa");
Integer num1 = function.apply("10");
System.out.println("num1 = " + num1);
}
}
默认方法:andThen
Function 接口中有一个默认的 andThen 方法,用来进行组合操作。JDK源代码如:
该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多:
package com.itheima.demo03functionalinterface;
import java.util.function.Function;
public class Demo06FunctionAndThen {
// 使用Lambda表达式将字符串转成数字, 第二个操作将这个数字乘以5
public static void main(String[] args) {
getNumber((String str) -> {
return Integer.parseInt(str);
}, (Integer i) -> {
return i * 5;
});
}
public static void getNumber(Function<String, Integer> f1, Function<Integer, Integer> f2) {
System.out.println("aa");
// Integer num = f1.apply("6");
// Integer num2 = f2.apply(num);
Integer num2 = f1.andThen(f2).apply("6");
System.out.println("num2 = " + num2);
}
}
第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过 andThen 按照前后顺序组合到了一 起。
请注意,Function的前置条件泛型和后置条件泛型可以相同。
1.7 Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用
java.util.function.Predicate 接口。
使用Lambda判断一个人名如果超过3个字就认为是很长的名字
对test方法的参数T进行判断,返回boolean类型的结果。用于条件判断的场景:
package com.itheima.demo03functionalinterface;
import java.util.function.Predicate;
public class Demo07Predicate {
// 使用Lambda判断一个人名如果超过3个字就认为是很长的名字
public static void main(String[] args) {
System.out.println("开始啦");
isLongName((String name) -> {
return name.length() > 3;
});
}
public static void isLongName(Predicate<String> predicate) {
System.out.println("aa");
boolean isLong = predicate.test("迪丽热巴");
System.out.println("是否是长名字: " + isLong);
}
}
条件判断的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长。
默认方法:and
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实
现“并且”的效果时,可以使用default方法 and 。其JDK源码为:
使用Lambda表达式判断一个字符串中即包含W,也包含H
使用Lambda表达式判断一个字符串中包含W或者包含H
使用Lambda表达式判断一个字符串中即不包含W
如果要判断一个字符串既要包含大写“H”,又要包含大写“W”,那么:
package com.itheima.demo03functionalinterface;
import java.util.function.Predicate;
public class Demo08Predicate_And_Or_Negate {
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
// 使用Lambda表达式判断一个字符串中包含W或者包含H
// 使用Lambda表达式判断一个字符串中不包含W
public static void main(String[] args) {
test((String str) -> {
// 判断是否包含W
return str.contains("W");
}, (String str) -> {
// 判断是否包含H
return str.contains("H");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
// String str = "Hello orld";
// boolean b1 = p1.test(str);
// boolean b2 = p2.test(str);
// if (b1 && b2) {
// System.out.println("即包含W,也包含H");
// }
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
String str = "Hello World";
boolean b = p1.and(p2).test(str);
if (b) {
System.out.println("即包含W,也包含H");
}
}
}
默认方法:or
使用Lambda表达式判断一个字符串中包含W或者包含H
与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:
如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:
package com.itheima.demo03functionalinterface;
import java.util.function.Predicate;
public class Demo08Predicate_And_Or_Negate {
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
// 使用Lambda表达式判断一个字符串中包含W或者包含H
// 使用Lambda表达式判断一个字符串中不包含W
public static void main(String[] args) {
test((String str) -> {
// 判断是否包含W
return str.contains("W");
}, (String str) -> {
// 判断是否包含H
return str.contains("H");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
// String str = "Hello orld";
// boolean b1 = p1.test(str);
// boolean b2 = p2.test(str);
// if (b1 && b2) {
// System.out.println("即包含W,也包含H");
// }
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
String str = "Hello World";
boolean b = p1.and(p2).test(str);
if (b) {
System.out.println("即包含W,也包含H");
}
// 使用Lambda表达式判断一个字符串中包含W或者包含H
boolean b1 = p1.or(p2).test(str);
if (b1) {
System.out.println("包含W或者包含H");
}
}
}
默认方法:negate
使用Lambda表达式判断一个字符串中即不包含W
“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法 negate 的JDK源代码为:
从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前调
用 negate 方法,正如 and 和 or 方法一样:
package com.itheima.demo03functionalinterface;
import java.util.function.Predicate;
public class Demo08Predicate_And_Or_Negate {
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
// 使用Lambda表达式判断一个字符串中包含W或者包含H
// 使用Lambda表达式判断一个字符串中不包含W
public static void main(String[] args) {
test((String str) -> {
// 判断是否包含W
return str.contains("W");
}, (String str) -> {
// 判断是否包含H
return str.contains("H");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
// String str = "Hello orld";
// boolean b1 = p1.test(str);
// boolean b2 = p2.test(str);
// if (b1 && b2) {
// System.out.println("即包含W,也包含H");
// }
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
String str = "Hello World";
boolean b = p1.and(p2).test(str);
if (b) {
System.out.println("即包含W,也包含H");
}
// 使用Lambda表达式判断一个字符串中包含W或者包含H
boolean b1 = p1.or(p2).test(str);
if (b1) {
System.out.println("包含W或者包含H");
}
// 使用Lambda表达式判断一个字符串中不包含W
boolean b2 = p1.negate().test("Hello W");
// negate相当于取反 !boolean
if (b2) {
System.out.println("不包含W");
}
}
}
1.8 小结
- Supplier接口
- Consumer接口
3. Function接口
4. Predicate接口