Lambda表达式
谓词(predicate)
在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回 true 或 false 。
你得用 for-each 循环一个个去迭代元素,然后再处理元素。我们把这种
数据迭代的方法称为外部迭代。相反,有了Stream API,你根本用不着操心循环的事情。数据处
理完全是在库内部进行的。我们把这种思想叫作内部迭代。
函数式编程中的函数的主要意思是“把函数作为一等值”,不过它也常常隐含着第二层意思,即“执行时在
元素之间无互动”。
以下哪些是使用Lambda表达式的有效方式?
(1) execute(() -> {});
public void execute(Runnable r){
r.run();
}
(2) public Callable<String> fetch() {
return () -> "Tricky example ;-)";
}
(3) Predicate<Apple> p = (Apple a) -> a.getWeight();1
(1) execute(() -> {});2
public void execute(Runnable r){3
r.run();4
}5
(2) public Callable<String> fetch() {6
return () -> "Tricky example ;-)";7
}8
(3) Predicate<Apple> p = (Apple a) -> a.getWeight();答案:只有1和2是有效的。
第一个例子有效,是因为Lambda () -> {} 具有签名 () -> void ,这和 Runnable 中的
抽象方法 run 的签名相匹配。请注意,此代码运行后什么都不会做,因为Lambda是空的!
第二个例子也是有效的。事实上, fetch 方法的返回类型是 Callable<String> 。
Callable<String> 基本上就定义了一个方法,签名是 () -> String ,其中 T 被 String 代替
了。因为Lambda () -> "Trickyexample;-)" 的签名是 () -> String ,所以在这个上下文
中可以使用Lambda。
第三个例子无效,因为Lambda表达式 (Apple a) -> a.getWeight() 的签名是 (Apple) ->
Integer ,这和 Predicate<Apple>:(Apple) -> boolean 中定义的 test 方法的签名不同。
使用Lambda的步骤
1. 一个方法,内部实现可能是重复的类似的,但又有点区别,就可以把有区别的部分,抽象成一个函数式接口(根据代码内部逻辑,找出对应的函数式接口签名 )。
2. 给这个方法提供一个使用函数式接口参数的版本
3. 然后依据这个函数式接口抽象方法的签名,写对应的Lambda表达式作为传入参数
Java在java.util.function包中已经提供了几个函数式接口:
1. predicate<T>返回boolean
2. Consumer<T>返回void
3. Function<T,R>返回R
Java 8中的常用函数式接口 | ||
函数式接口 | 函数描述符 | 原始类型特化 |
Predicate<T> | T->boolean | IntPredicate,LongPredicate, DoublePredicate |
Consumer<T> | T->void | IntConsumer,LongConsumer, DoubleConsumer |
Function<T,R> | T->R | IntFunction<R>, IntToDoubleFunction, IntToLongFunction, LongFunction<R>, LongToDoubleFunction, LongToIntFunction, DoubleFunction<R>, ToIntFunction<T>, ToDoubleFunction<T>, ToLongFunction<T> |
Supplier<T> | ()->T | BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier |
UnaryOperator<T> | T->T | IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator |
BinaryOperator<T> | (T,T)->T | IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator |
BiPredicate<L,R> | (L,R)->boolean | |
BiConsumer<T,U> | (T,U)->void | ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T> |
BiFunction<T,U,R> | (T,U)->R | ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U> |
Java 8的Lambda和匿名类可以做类似于闭包的事情:
它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但有一个限制:它们不
能修改定义Lambda的方法的局部变量的内容。这些变量必须是隐式最终的。可以认为Lambda
是对值封闭,而不是对变量封闭。如前所述,这种限制存在的原因在于局部变量保存在栈上,
并且隐式表示它们仅限于其所在线程。如果允许捕获可改变的局部变量,就会引发造成线程
不安全的新的可能性,而这是我们不想看到的(实例变量可以,因为它们保存在堆中,而堆
是在线程之间共享的)。


浙公网安备 33010602011771号