一. Lambda定義(λ):java
-- 匿名,它不像普通方法那樣有一個明確的名稱;express
-- 函數,它不像普通方法那樣屬於某個特定的類,但和方法同樣,Lambda有參數列表、函數主體、返回類型或拋出異常列表:app
-- 傳遞,Lambda能夠做爲參數傳遞給方法或存儲在變量中:ide
-- 簡潔。函數
二. Lambda表達式結構:性能
1. 參數列表;spa
2. 箭頭:箭頭->把參數列表與Lambda主體分隔開;線程
3. Lambda主體:表達式就是Lambda表達式的例子。對象
三.Lambda基本語法:排序
(parameters) -> expression
或
(parameters) -> { statements; }
使用顯式返回語句時須要使用花括號「{}」。
eg: Lambda示例:
使用案例 | Lambda示例 |
無參數,返回void | () -> {} |
無參數,返回String | () -> "Raoul" |
無參數,返回String(利用顯式返回語句) | () -> { return "Result";} |
布爾表達式 | (List<String> list) -> list.isEmpty() |
建立對象 | () -> new Apple(10); |
消費一個對象 | (Apple apple) -> { System.out.println(a.getWeight()); } |
從一個對象中選擇/抽取 | (String s) -> s.length() |
組合兩個值 | (int a, int b) -> a * b |
比較兩個對象 | (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) |
四. 在哪裏使用Lambda
能夠在函數式接口上使用Lambda表達式。
函數式接口:只定義一個抽象方法的接口。
@FunctionalInterface:代表爲函數式接口,但它不是必須的。
(T, U) -> R表達式展現了應當如何思考函數描述符。左側表明了參數類型。這裏它表明一個函數,具備兩個參數,分別爲泛型T和U,返回類型爲R。
Java的泛型只能綁定到引用類型,當須要引用原始類型時由於自動裝箱和拆箱機制時,裝箱後的值須要更多的內存,須要付出性能代價,爲了不裝箱操做對Predicate<T>和Function<T, R>等通用函數式接口的原始類型特化:IntPredicate、IntToLongFunction等。
Java8中的經常使用函數式接口:
函數式接口 | 函數描述符 | 原始類型特化 |
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 | ObjectIntConsumer<T>, ObjectLongConsumer<T>, ObjectDoubleConsumer<T> |
BiFunction<T, U, R> | (T, U) -> R | ToIntBiFunction<T, U>, ToLongBiFunction<T, U>, ToDoubleBiFunction<T, U> |
Runnable | () -> void |
當須要Lambda表達式拋出異常時,有兩種方式:
-- 本身編寫新的函數式接口,並聲明受檢異常(任何函數式接口都不容許拋出受檢異常);
-- 將Lambda包在一個try/catch塊中。
@FunctionalInterface public interface BufferedReaderProcessor { String process(BufferedReader b) throws IOException; }
Function<BufferedReader, String> f = (BufferedReader b) -> { try { return b.readLine(); } catch (IOException e) { throw new RuntimeException(e); } };
當Lambda表達式拋出一個異常時,throws語句也必須與Lambda所指類型相匹配。
若是Lambda的主體是一個語句表達式,它就和一個返回void的函數描述符兼容(固然須要參數列表也兼容)。
eg:儘管List的add方法返回的是boolean,但如下兩行都是合法的:
//Predicate返回了一個boolean Predicate<String> p = s -> list.add(s); //Consumer返回了一個void Consumer<String> b = s -> list.add(s);
Lambda類型推斷:Java編譯器會從上下文(目標類型)中推斷出用什麼函數式接口來配合Lambda表達式,因此也能推斷出適合Lambda的簽名,由於函數描述符能夠經過目標類型來獲得。這樣就能夠在Lambda中省去標註參數類型,當參數只有一個時還能夠省去參數的括號。
Lambda使用局部變量的限制:Lambda表達式對值封閉,而不是對變量封閉。Lambda表達式引用的局部變量必須是final的,只能有一次賦值。
這是由於實例變量是儲存在堆中,而局部變量是儲存在棧上。若是Lambda能夠直接訪問局部變量,並且Lambda是在一個線程中使用的,則使用Lambda的線程可能會在分配該變量的線程將這個變量收回以後,去訪問該變量。所以,Java在訪問自由局部變量時,其實是在訪問他的副本而不是訪問原始變量。
Lambda方法引用:
方法引用就是Lambda的快捷寫法。目標引用放在分隔符::前面,方法的名稱放在後面。
eg:
Lambda表達式 | 等效的方法引用 |
(Apple a) -> a.getWeight() | Apple::getWeight |
() -> Thread.currentThread().dumpStack() | Thread.currentThread()::dumpStack |
(str, i) -> str.substring(i) | String::substring |
(String s) -> System.out.println(s) | System.out::println |
如何構建方法引用:
主要有三類:
-- 指向靜態方法的放方法引用;
-- 指向任意類型實例方法的方法引用;
-- 指向現有對象的實例方法的方法引用。
Lambda構造函數引用:
對於一個現有構造函數,能夠利用它的名稱和關鍵字new來建立它的一個引用:ClassName::new。它的功能與靜態方法的引用相似。
Lambda表達式 | 等價構造方法引用Lambda表達式 |
Supplier<Apple> c1 = Apple::new; Apple a1 = c1.get(); |
Supplier<Apple> c1 = () -> new Apple(); Apple a1 = c1.get(); |
Function<Integer, Apple> c2 = Apple::new; Apple a2 = c2.apply(110); |
Function<Integer, Apple> c2 = (weight) -> new Apple(weight); Apple a2 = c2.apply(110); |
BiFunction<String, Integer, Apple> c2 = Apple::new; Apple a2 = c2.apply("green", 110); |
BiFunction<String, Integer, Apple> c2 = (color, weight) -> new Apple(color, weight); Apple a2 = c2.apply("green", 110); |
複合Lambda表達式的有用方法:
複合類型 | 方法 | 說明 | 舉例 |
比較器複合 | reversed() | 逆序 | //按重量遞減排序 inventory.sort(comparing(Apple::getWeight)) .reversed(); |
thenComparing | 比較器鏈(接受一個函數做爲參數,若是兩個對象用第一個Comparator比較以後是同樣的,就提供第二個Comparator | //兩個蘋果同樣重時按國家排序 inventory.sort(comparing(Apple::getWeight)) .reversed(). thenComparing(Apple::getCountry); |
|
謂詞複合 (and和or方法的優先級是按照在表達式鏈中的位置,從左向右肯定的) |
negate | 非 | //產生現有對象redApple的非 Predicate<Apple> notRedApple = redApple.negate(); |
and | 將兩個Lambda用and組合起來 | //一個蘋果既是紅色又比較重(連接兩個謂詞來生成一個Predicate對象) Predicate<Apple> RedAndHeavyApple = redApple.and(a -> a.getWeight() > 150); |
|
or | 要麼 | //要麼重(150g以上)的紅蘋果,要麼是綠蘋果 Predicate<Apple> RedAndHeavyApple = redApple.and(a -> a.getWeight() > 150) .or(a -> "green".equals(a.getColor())); |
|
函數複合 | andThen | 先對輸入應用一個給定函數,再對輸出應用另外一個函數 | //等同於數學上的g(f(x)),返回4 Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h = f.andThen(g); int result = h.apply(1); |
compose | 先把給定的函數用做compose的參數裏面給的那個函數,而後再把函數自己用於結果 | //等同於數學上的f(g(x)),返回3 Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h = f.compose(g); int result = h.apply(1); |