Lambda.jpg
使用Lambda表達式,我們可以很簡潔地傳遞代碼(通常是匿名函數)。
結構
Lambda表達式主要分為三部分:參數列表,箭頭,Lambda 主體
語法
- (parameters) -> expression
- (parameters) -> { statements; }
如果表達式只有一行,用第一種,多行用第二種。
Java8中,標注了@FunctionalInterface
,表明這個接口將是一個函數式接口,它里面只能有一個抽象方法。
常用的函數式接口
JDK已經為我們提供了很多常用的函數式接口:
- Predicate:
java.util.function.Predicate<T>
接口定義了一個名叫test的抽象方法,它接受泛型T對象,并返回一個boolean。在需要表示一個涉及類型T的布爾表達式時可以使用。 - Consumer:
java.util.function.Consumer<T>
定義了一個名叫accept的抽象方法,它接受泛型T的對象,沒有返回(void)。如果需要訪問類型T的對象,并對其執行某些操作,就可以使用這個接口。 - Supplier:
java.util.function.Supplier<T>
不接受對象,返回一個泛型對象T。在需要new一個對象實例時可以使用。 - Function:
java.util.function.Function<T, R>
接口定義了一個叫作apply的方法,它接受一個泛型T的對象,并返回一個泛型R的對象。如果需要定義一個Lambda,將輸入對象的信息映射到輸出,就可以使用這個接口。
原始類型特化
我們知道,泛型只能綁定到引用類型的對象。因此,在使用泛型綁定基本類型的時候,Java會為我們自動裝箱和拆箱,但這是會消耗性能的。
如果輸入和輸出都是基本類型時,Java8為我們提供了新的函數式接口,以避免自動裝箱拆箱。
簡單列舉一部分:
- Predicate:
IntPredicate
,LongPredicate
,DoublePredicate
- Consumer:
IntConsumer
,LongConsumer
,DoubleConsumer
- Supplier:
BooleanSupplier
,IntSupplier
,LongSupplier
,DoubleSupplier
- Function:
IntFunction<R>
,LongToDoubleFunction
,ToLongFunction<T>
從命名可以輕易看出從什么類型轉成什么類型,可以在java.util.function
包下查看所有接口。
使用局部變量
在使用lambda時,主體代碼塊內允許使用的外部變量。但是,不允許改變外部變量。這些變量應該聲明為final
或者事實上是final
的(即之后代碼中不會改變)
方法引用
方法引用主要有三類:
- 指向靜態方法的方法引用
- Lambda:
(args) -> ClassName.staticMethod(args)
- 方法引用:
ClassName :: staticMethod
- Lambda:
- 指向任意類型實例方法的方法引用
- Lambda:
(arg0, rest) -> arg0.instanceMethod(rest)
- 方法引用:
ClassName :: instanceMethod
(arg0 是 ClassName 類型的)
- Lambda:
- 指向現有對象的實例方法的方法引用
- Lambda:
(args) -> expr.instanceMethod(args)
- 方法引用:
expr :: intanceMethod
- Lambda:
除此之外,還有構造函數引用:ClassName :: new
比如用Map來將構造函數映射到字符串值:
static Map<String, Function<Integer, Fruit>> map = new HashMap<>();
static {
map.put("apple", Apple::new);
map.put("orange", Orange::new);
// etc...
}
public static Fruit giveMeFruit(String fruit, Integer weight) {
return map.get(fruit.toLowerCase()).apply(weight);
}
復合 Lambda 表達式
Comparator、Predicate和Function等函數式接口都有幾個可以用來結Lambda表達式的默認方法。
比較器復合
- 普通排序
comparing()
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
- 逆序
reversed()
inventory.sort(comparing(Apple::getWeight).reversed());
- 比較器鏈
thenComparing()
inventory.sort(comparing(Apple::getWeight).reversed()
.thenComparing(Apple::getCountry));
謂詞復合
3個方法增強已有的Predicate接口:
-
and
:與 -
or
:或 -
negate
:非
請注意,and和or方法是按照在表達式鏈中的位置,從左向右確定優先級的。因此,a.or(b).and(c)可以看作(a || b) && c。
函數復合
Function接口有andThen
和compose
兩個默認方法,它們都會返回Function的一個實例。
舉個例子:
有2個函數,一個加1,一個乘2
Function<Integer, Integer> f = x -> x + 1; // f(x)=x+1
Function<Integer, Integer> g = x -> x * 2; // g(x)=2x
andThen()
Function<Integer, Integer> h = f.andThen(g); // g(f(x))
int result = h.apply(1); // 4
compose()
Function<Integer, Integer> h = f.compose(g); // f(g(x))
int result = h.apply(1); // 3