Lambda表達式能夠理解爲一種匿名函數:它沒有名稱,但有參數列表、函數主體、返回類型,可能還有一個能夠拋出的異常的列表,能夠簡潔地傳遞代碼。java
•匿名——咱們說匿名,是由於它不像普通的方法那樣有一個明確的名稱:寫得少而想得多!
•函數——咱們說它是函數,是由於Lambda函數不像方法那樣屬於某個特定的類。但和方法同樣,Lambda有參數列表、函數主體、返回類型,還可能有能夠拋出的異常列表。
•傳遞——Lambda表達式能夠做爲參數傳遞給方法或存儲在變量中。
•簡潔——無需像匿名類那樣寫不少模板代碼。
好比,利用Lambda表達式,能夠更爲簡潔地自定義一個Comparator對象。程序員
// 使用匿名類,按照重量升序對庫存排序 inventory.sort(new Comparator<Apple>() { public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo(a2.getWeight()); } }); System.out.println(inventory); // 用Lambda表達式,按照重量升序對庫存排序 inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())); System.out.println(inventory);
Lambda表達式有三個部分:express
•參數列表——這裏它採用了Comparator中compare方法的參數,兩個Apple。
•箭頭——箭頭->把參數列表與Lambda主體分隔開。
•Lambda主體——比較兩個Apple的重量。表達式就是Lambda的返回值了。
爲了進一步說明,下面給出了Java 8中五個有效的Lambda表達式的例子。ide
//Java 8中有效的Lambda表達式 //第一個Lambda表達式具備一個String類型的參數並返回一個int。Lambda沒有return語句,由於已經隱含了return (String s) -> s.length() //第二個Lambda表達式有一個Apple 類型的參數並返回一個boolean(蘋果的重量是否超過150克) (Apple a) -> a.getWeight() > 150 //第三個Lambda表達式具備兩個int類型的參數而沒有返回值(void返回)。注意Lambda表達式能夠包含多行語句,這裏是兩行 (int x, int y) -> { System.out.println("Result:"); System.out.println(x+y); } //第四個Lambda表達式沒有參數,返回一個int () -> 42 //第五個Lambda表達式具備兩個Apple類型的參數,返回一個int:比較兩個Apple的重量 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
Java語言設計者選擇這樣的語法,是由於C#和Scala等語言中的相似功能廣受歡迎。Lambda的基本語法是(parameters) -> expression或(請注意語句的花括號)(parameters) -> { statements; }函數
Lambda表達式能夠被賦給一個變量,或傳遞給一個接受函數式接口做爲參數的方法,這個Lambda表達式的簽名要和函數式接口的抽象方法同樣spa
可能會想:「爲何只有在須要函數式接口的時候才能夠傳遞Lambda呢?」語言的設計者也考慮過其餘辦法,例如給Java添加函數類型。可是他們選擇瞭如今這種方式,由於這種方式天然且能避免語言變得更復雜。此外,大多數Java程序員都已經熟悉了具備一個抽象方法的接口的理念(例如事件處理)。 設計
附:只有一個抽象方法的接口爲函數式接口,函數式接口的抽象方法的簽名基本上就是Lambda表達式的簽名。咱們將這種抽象方法叫做函數描述符。例如,Runnable接口能夠看做一個什麼也不接受什麼也不返回(void)的函數的簽名,由於它只有一個叫做run的抽象方法,這個方法什麼也不接受,什麼也不返回(void)。3d
附: @FunctionalInterface又是怎麼回事? 若是去看看新的Java API,會發現函數式接口帶有@FunctionalInterface的標註。這個標註用於表示該接口會設計成一個函數式接口。若是用@FunctionalInterface定義了一個接口,而它卻不是函數式接口的話,編譯器將返回一個提示緣由的錯誤。例如,錯誤消息多是「Multiple nonoverriding abstract methods found in interface Foo」,代表存在多個抽象方法。請注意,@FunctionalInterface不是必需的,但對於爲此設計的接口而言,使用它是比較好的作法。它就像是@Override標註表示方法被重寫了。code
內置函數式接口
方法引用讓能夠重複使用現有的方法定義,並像Lambda同樣傳遞它們。在一些狀況下,比起使用Lambda表達式,它們彷佛更易讀,感受也更天然。下面就是咱們藉助更新的Java 8API,用方法引用寫的一個排序的例子:對象
先前:
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
以後(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
方法引用能夠被看做僅僅調用特定方法的Lambda的一種快捷寫法。它的基本思想是,若是一個Lambda表明的只是「直接調用這個方法」,那最好仍是用名稱來調用它,而不是去描述如何調用它。事實上,方法引用就是讓根據已有的方法實現來建立Lambda表達式。可是,顯式地指明方法的名稱,的代碼的可讀性會更好。它是如何工做的呢?當須要使用方法引用時,目標引用放在分隔符::前,方法的名稱放在後面。
能夠把方法引用看做針對僅僅涉及單一方法的Lambda的語法糖,由於表達一樣的事情時要寫的代碼更少了。 如何構建方法引用 方法引用主要有三類。
(1)指向靜態方法的方法引用(例如Integer的parseInt方法,寫做Integer::parseInt)。
(2)指向任意類型實例方法的方法引用(例如String的length方法,寫做String::length)。
(3)指向現有對象的實例方法的方法引用(假設有一個局部變量expensiveTransaction用於存放Transaction類型的對象,它支持實例方法getValue,那麼就能夠寫expensiveTransaction::getValue)。
第二種和第三種方法引用可能乍看起來有點兒暈。相似於String::length的第二種方法引用的思想就是在引用一個對象的方法,而這個對象自己是Lambda的一個參數。例如,Lambda表達式(String s) -> s.toUppeCase()能夠寫做String::toUpperCase。但第三種方法引用指的是,在Lambda中調用一個已經存在的外部對象中的方法。例如,Lambda表達式()->expensiveTransaction.getValue()能夠寫做expensiveTransaction::getValue。