能夠把Lambda表達式理解爲簡潔地表示可傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表、函數主體、返回類型,可能還有一個能夠拋出的異常列表。html
(parameters) -> expression 或 (parameters) -> { statements; }
eg:(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Lambda表達式有三個部分:java
函數式接口就是隻定義一個抽象方法的接口。接口上標有@FunctionalInterface
表示該接口會設計成 一個函數式接口,若是你用@FunctionalInterface
定義了一個接口,而它卻不是函數式接口的話,編譯器將返回一個提示緣由的錯誤。接口如今還能夠擁有默認方法(即在類沒有對方法進行實現時, 其主體爲方法提供默認實現的方法)。哪怕有不少默認方法,只要接口只定義了一個抽象方法,它就仍然是一個函數式接口。程序員
函數式接口的抽象方法的簽名就是Lambda表達式的簽名。咱們將這種抽象方法叫做:函數描述符。例如,Runnable接口能夠看做一個什麼也不接受什麼也不返回(void)的函數的簽名,由於它只有一個叫做run的抽象方法,這個方法什麼也不接受,什麼也不返回(void)。express
/**
* Represents a predicate (boolean-valued function) of one argument.
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
* @param <T> the type of the input to the predicate
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}複製代碼
Predicate的英文示意是:謂詞。Predicate接口定義了一個名叫test的抽象方法,它接受泛型T對象,並返回一個boolean。編程
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
* @param <T> the type of the input to the operation
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
* @param t the input argument
*/
void accept(T t);
}複製代碼
Consumer的英文示意是:消費者。Consumer接口定義了一個名叫accept的抽象方法,它接受泛型T對象,並無返回任何值。app
/**
* Represents a function that accepts one argument and produces a result.
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
* @param t the function argument
* @return the function result
*/
R apply(T t);
}複製代碼
Function的英文示意是:功能。Function接口定義了一個名叫apply的抽象方法,它接受泛型T對象,並返回一個泛型R的對象。ide
Java還有一個自動裝箱機制來幫助程序員執行這一任務:裝箱和拆箱操做是自動完成的。但這在性能方面是要付出代價的。裝箱後的值本質上就是把原始類型包裹起來,並保存在堆裏。所以,裝箱後的值須要更多的內存,並須要額外的內存搜索來獲取被包裹的原始值。Java 8爲咱們前面所說的函數式接口帶來了一個專門的版本,以便在輸入和輸出都是原始類型時避免自動裝箱的操做。函數
Lambda的類型是從使用Lambda的上下文推斷出來的。上下文(好比,接受它傳遞的方法的參數,或接受它的值的局部變量)中Lambda表達式須要的類型稱爲目標類型。類型檢查過程能夠分解爲以下所示。性能
這段代碼是有效的,由於咱們所傳遞的Lambda表達式也一樣接受Apple爲參數,並返回一個 boolean。請注意,若是Lambda表達式拋出一個異常,那麼抽象方法所聲明的throws語句也必 須與之匹配。有了目標類型的概念,同一個Lambda表達式就能夠與不一樣的函數式接口聯繫起來,只要它 們的抽象方法簽名可以兼容。好比,前面提到的Callable和PrivilegedAction,這兩個接口都表明着什麼也不接受且返回一個泛型T的函數。 所以,下面兩個賦值是有效的:this
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;複製代碼
特殊的void兼容規則若是一個Lambda的主體是一個語句表達式, 它就和一個返回void的函數描述符兼容(固然須要參數列表也兼容)。例如,如下兩行都是合法的,儘管List的add方法返回了一個 boolean,而不是Consumer上下文(T -> void)所要求的void:
// Predicate返回了一個boolean
Predicate<String> p = s -> list.add(s);
// Consumer返回了一個void
Consumer<String> b = s -> list.add(s);複製代碼
Java編譯器會從上下文(目標類型)推斷出用什麼函數式接 口來配合Lambda表達式,這意味着它也能夠推斷出適合Lambda的簽名,由於函數描述符能夠經過目標類型來獲得。這樣作的好處在於,編譯器能夠了解Lambda表達式的參數類型,這樣就能夠在Lambda語法中省去標註參數類型。
// 沒有類 型推斷
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
// 有類型推斷
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight()); 複製代碼
Lambda表達式 也容許使用自由變量(不是參數,而是在外層做用域中定義的變量),就像匿名類同樣。 它們被 稱做捕獲Lambda。Lambda捕獲的局部變量必須顯式聲明爲final, 或事實上是final。換句話說,Lambda表達式只能捕獲指派給它們的局部變量一次。
方法引用能夠被看做僅僅調用特定方法的Lambda的一種快捷 寫法,方法引用看做針對僅僅涉及單一方法的Lambda的語法糖。目標引用放在分隔符::前,方法的名稱放在後面。方法引用主要有三類:
對於一個現有構造函數,你能夠利用它的名稱和關鍵字new來建立它的一個引用: ClassName::new。它的功能與指向靜態方法的引用相似。
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
這就等價於:
Supplier<Apple> c1 = () -> new Apple(); // 利用默認構造函數建立 Apple的Lambda表達式
Apple a1 = c1.get(); // 調用Supplier的get方法 將產生一個新的Apple複製代碼
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
// 逆序 按重量遞 減排序
inventory.sort(comparing(Apple::getWeight).reversed());
// 比較器鏈 按重量遞減排序;兩個蘋果同樣重時,進一步按國家排序
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));複製代碼
// 產生現有Predicate 對象redApple的非
Predicate<Apple> notRedApple = redApple.negate();
// 連接兩個謂詞來生成另 一個Predicate對象 一個蘋果既是紅色又比較重
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
// 連接Predicate的方法來構造更復雜Predicate對象 表達要麼是重(150克以上)的紅蘋果,要麼是綠蘋果
Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));
請注意,and和or方法是按照在表達式鏈中的位置,從左向右肯定優 先級的。所以,a.or(b).and(c)能夠看做(a || b) && c。複製代碼
andThen方法會返回一個函數,它先對輸入應用一個給定函數,再對輸出應用另外一個函數。 好比,
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);
數學上會寫做g(f(x))或(g o f)(x)
這將返回4
compose方法,先把給定的函數用做compose的參數裏面給的那個函 數,而後再把函數自己用於結果。
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);
數學上會寫做f(g(x))或(f o g)(x)
這將返回3複製代碼
如下是你應從本章中學到的關鍵概念。