天天看點

一起爪哇Java 8(二)——Lambda表達式和方法引用

java 表達式有很多種,聲明一個class是一個表達式,定義一個變量是一個表達式,寫一個=指派邏輯是一個表達式……

lambda表達式是這樣一個表達式:

lambdaparameters -> lambdabody

在lambdaparameters傳遞參數,在lambdabody中編寫邏輯。lambda表達式生成的結果就是一個函數式接口(上文提到過的)。lambdabody中的邏輯内容(各種表達式)不會在定義時執行,在實際函數式接口調用時才會執行。

舉幾個官方的例子看看:

可以通過上面的例子看到,lambda的參數聲明主要包含兩大類,一類是聲明類型的,一類是不聲明類型的(依賴推斷的)。其中聲明類型的參數,與定義一個方法時聲明參數是一樣的。

幾個注意的點:

_不能作為lambda參數。

int...與int[]是一緻的。

當參數是推斷類型時,注意推斷類型的類型轉換錯誤,類型是依據上下文變化的。

來個推斷的例子:

body部分的形式同一個方法的描述基本一緻,或者是一個表達式,或者是一個block代碼。整體了解lambda的參數和body,可以對應上一節的function接口來看:()的參數部分,對應function的第一個泛型參數;{}或者類似x+1這樣的表達式作為body,對應function的第二個泛型參數。空參數對應supplier,而空return對應consumer。

不同于匿名内部類的形式,lambda表達式的body共享上下文類的this變量。另一個注意點是lambda表達式的body裡包含的外部變量,變量需要是final的或者effectively final。

effectively final的定義如下:

如果是有初始值的變量(指派過一次),需要滿足:

沒有聲明final

從未出現在指派語句的左值部分

從未作為一個變量被++或--之類的遞增遞減形式操作過

如果是沒有初始值的變量,需要滿足:

在實際指派前,絕對未指派或者未絕對指派

方法、構造函數、lambda和異常的參數,會被認為是effectively final

這裡又引入兩個概念:絕對指派和絕對未指派。

絕對指派:變量在複雜邏輯中的每個執行路徑中都保證指派語句存在。

絕對未指派:變量在複雜邏輯中的每個執行路徑中都保證沒有指派語句存在。

看個例子:(絕對指派,需要注釋掉n=6)

不滿足絕對指派:

絕對未指派:

不滿足絕對未指派:

body部分也表達出了一部分相容性,即當body部分是表達式語句時,如果語句允許獨立執行,那麼該表達式等價于body部分是void傳回值的。即如下的例子,list.add是個傳回boolean的方法,因為可以獨立執行,那麼下面的例子都是ok的:

方法引用表達式是另一類執行函數式接口的模式,在java 8之前是沒有能力表達一個函數方法的,在java 8引入函數式接口後,每個lambda表達式都代表了一個函數,可以指向性的将lambda表達式指派給一個function類的接口。另一個重要的方法就是直接使用函數方法引用。

方法引用是通過[對象名]::[方法名]這種模式來引用的,其中::兩個冒号的操作符非常重要。具體的場景針對類、對象執行個體、數組、泛型等均有不同的支援,下面的例子看看各種方法引用的表達方式:

其中需要注意的是,數組的new方法引用等價于一個有入參的function,因為new一個數組是需要指定size的。

無論lambda表達式還是方法引用表達式,所指向的都是一個方法或者是函數。而它們指向的内容能指派的也一定是函數式接口。這兩種指向也是實用場景各異,方法引用需要使用在已有方法上(顯而易見),而lambda表達式是一種快速行内聲明一個方法且指向一個函數式接口的方法。兩者互動配合,基本可以覆寫各種函數式接口使用的場景。