Java8 Lambda表達式

 

一,java8爲何會出現Lambda表達式

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表達式能夠被賦給一個變量,或傳遞給一個接受函數式接口做爲參數的方法,這個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同樣傳遞它們。在一些狀況下,比起使用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。

相關文章
相關標籤/搜索