目标
我们的最终目的是理解这段代码:
Optional<String> tes = Optional.empty();
tes.ifPresent(System.out::println);
// 库实现
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}
Consumer + Accept
只定义输入参数的类型,没有返回值类型定义,所以 accept 行为上也不返回值,只执行函数。
@FunctionalInterface
interface Consumer<T> {
void accept(T t); // 无返回值,仅执行操作
}
Function + Apply
apply 就是向 Function<String,String> 的函数实例传递输入值,得到 transformer 的返回值。
import java.util.Optional;
import java.util.function.Function;
@FunctionalInterface
interface StringTransformer {
String transFormer(String input);
}
class StringProcessor {
String processor(String input, Function<String,String> tranformer) {
return tranformer.apply(input);
}
}
class toUpper {
public static String toUpp(String input) {
return input.toUpperCase();
}
}
class Solution {
public static void main(String[] args) {
StringProcessor stringProcessor = new StringProcessor();
String result = stringProcessor.processor("hello", toUpper::toUpp);
System.out.println(result);
}
}
函数式接口 VS Lambda VS 方法引用
Lambda 对象用来实现函数式接口,方法引用作为参数会被编译器展开包装成为 Lambda 对象。
PECS 原则
- PECS = Producer Extends, Consumer Super
- 生产者用 extends,消费者用 super
- 如果你从集合里“生产”东西(拿出来,只读),用
? extends T;(生产出的数据只能是当前类型或当前类型的子类) - 如果你往集合里“消费”东西(放进去,只写),用
? super T。(子类有的其父类也有,消费安全)
为什么拿是生产,放是消费?
生产消费是相对于传入的容器来说的,容器生产东西你拿(生产者),或者是容器消费你放进去的东西(消费者)。
为什么子类只读,父类只写?
保证类型安全:类比权限系统,写入的对象类型必须包括容器类型中的所有类型的公共部分(最小权限原则),读取的类型必须包括容器中所有可能的类型(最大权限原则)。
比如说容器中是 List<? extends Number>,那其中可能是 Double, Integer 这些子类类型,
- 写入时:写入
Double,但是实际存的是Integer,出错; - 读取时:无论读出什么都是
Number的子类,都可以当做Number来用。
如果容器中是List<? super String>,那其中可能是String, Serializable, Object这些父类类型, - 写入时:写入
Object,知道无论容器中是什么类型,写入String这个已定义类型一定是安全的; - 读取时:不能确定读取的确切类型。
配合方法引用
现在可以理解最初的目标,ifPresent 作为数据的消费者,希望接收一个 Consumer<? super T>。在初始化 Optional 对象的时候,就指定了泛型类型为 String:Optional<String> tes = Optional.of("test");,调用 ifPresent 的时候传入 System.out::println 这个方法引用,编译器处理这个语法糖,将其展开为 lambda 对象:ifPresent(s -> System.out.println(s)),这个 lambda 对象和 Consumer 这个函数式接口兼容(参数数量、参数类型、返回值类型能匹配上某个函数式接口的唯一抽象方法,lambda 就可以用来实现它)。然后通过非 null 检查后,通过 accept 调用方法,并传递打印方法的参数,最终输出结果。
2130

被折叠的 条评论
为什么被折叠?



