Java 8 Lambda 表達式詳解


一、Lambda 表達式

1、基礎語法

Lambda 表達式基礎語法:

(parameters) -> expression
或 (parameters) ->{ statements; }

先理解:這段代碼可理解為一個方法,小括號里的內容是方法入參,大括號里的內容是方法體。 而這行代碼,就是一個 Lambda 表達式。所以 Lambda 表達式實際是一個方法(即函數)。

Java 8 中規定:Lambda 允許把函數(即Lambda 表達式)作為一個方法的參數(類似 JS 中的閉包)。 所以:Lambda 表達式是一個對象,而這個對象實際是一個方法。所以,Lambda 表達式是一個方法對象。

但是到底是什么對象呢?這個后續再細說。

2、語法簡寫形式

既然是一個方法,那么對自然有一些簡寫形式,如下:

  1. 可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。 即()里面無需聲明參數類型。()里面甚至可以什么都沒有

  2. 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。 即()可以省略。

  3. 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。 即{}可以省略,同時語句后面的分號也可以省略。

  4. 可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。

就是說,小括號中的入參可省略,小括號可省略,大括號可省略,大括號中的內容還可省略。 注:小括號和小括號中的入參不能同時省略。怎么省略可詳細參考:Java 8 Lambda 表達式 | 菜鳥教程

3、其他語法注意:

  1. lambda 表達式只能引用標記了 final 的外層局部變量。即不能在 lambda 內部修改定義在域外的局部變量,否則會編譯錯誤。 注:修改final肯定會報錯。另外,基本類型final就是final,引用類型final只表示變量地址(即引用)不可變,變量指向的內容還是實際可變的。

  2. 可以在 lambda 表達式中直接訪問外層的局部變量: 注:final肯定可以被直接引用了。

  3. lambda 表達式的局部變量可以不用聲明為 final,但是必須不可被后面的代碼修改(即隱性的具有 final 的語義) 注:這點應該由編譯來保證。即在lambda 表達式內部使用的局部變量肯定為final,即使不顯式聲明。

  4. 在 Lambda 表達式當中不允許聲明一個與局部變量同名的參數或者局部變量。 注:這點肯定是的。同名肯定會出錯。

二、方法引用和Lambda的關系

2.1 什么是方法引用?

  1. 方法引用是 lambda 表達式的一種特殊形式,即方法引用就是 Lambda 表達式(方法引用是 lambda 表達式的一種語法糖)。

  2. 方法引用用於獲取一個已有方法的 Lambda 表達式形式。

上面兩句應該說明了什么是方法引用。

2.2 方法引用的使用語法和限制

注:大寫開頭表示類名,小寫開頭表示對象。

  • 引用靜態方法 ContainingClass::staticMethodName 例子: String::valueOf,對應的 Lambda:(s) -> String.valueOf (s) 比較容易理解,和靜態方法調用相比,只是把.換為::

  • 引用特定對象的實例方法 containingObject::instanceMethodName 例子: x::toString,對應的 Lambda:() -> this.toString () 與引用靜態方法相比,都換為實例的而已

  • 引用特定類型的任意對象的實例方法(不建議使用,比較麻煩) ContainingType::methodName 例子: String::toString,對應的 Lambda:(s) -> s.toString () 實例方法要通過對象來調用,方法引用對應 Lambda,Lambda 的第一個參數會成為調用實例方法的對象。

  • 引用構造函數 ClassName::new 例子: String::new,對應的 Lambda:() -> new String () 構造函數本質上是靜態方法,只是方法名字比較特殊。

注意:第一和第三的區別在於::后面的方法是靜態還是非靜態,其他一致。

參考:Java 8 之方法引用 (Method References) - 永無止境,上下求索 - CSDN 博客

三、功能接口

3.1 功能接口定義

上面,一直說Lambda 表達式是一個對象,那是什么類型的對象呢? 這個類型就是功能接口類型。

在 Java 中,功能接口(Functional interface)指只有一個抽象方法的接口。 即如果一個接口只有一個抽象方法,Java 會自動認為該接口是功能接口。同時,該接口可以作為 Lambda 表達式的對象類型。 所以:Lambda 表達式是功能接口類型的對象,但這個功能接口類型不只一個,而是一類,這點要注意。

這里分析一下:Lambda 表達式既是方法(實現),又是對象。是什么方法(實現)?功能接口的方法(實現)。是什么類型接口的對象?功能接口類型的對象。 到這里,一切就清晰了。

那如何確定一個接口是不是功能接口呢?

  • 可以用@FunctionalInterface 注解標識。如果被標識的接口不符合功能接口規則,那么是會編譯報錯的。

3.2 JDK 中已有的功能接口

首先:這 4 個接口都是功能接口,所以其只有一個抽象方法。而這 4 個接口的不同在於抽象方法的參數和返回結果不同。理解了這點,就看也理解這四個接口了。

  1. Function.java 主要方法:R apply(T t); 功能說明: 將 Function 對象應用到輸入的參數上,然后返回計算結果。

  2. Consumer.java 主要方法:void accept(T t); 功能說明: 該接口表示接受單個輸入參數並且沒有返回值的操作。

  3. Predicate.java 主要方法:boolean test(T t); 功能說明: 表示判斷輸入的對象是否符合某個條件。

  4. Supplier.java 主要方法:T get(); 功能說明: 無參數,返回一個結果。

總結一下上面的4個接口的區別:

接口 參數 返回結果
Function
Consumer
Predicate 布爾類型
Supplier

參考:JDK8 函數式接口 Function、Consumer、Predicate、Supplier - 酒肉猿 - CSDN 博客

四、使用實例:

參考:例子可參考:Java 8 Lambda 表達式詳解 - 程序員干貨 - SegmentFault 思否 到這里,你應該就知道可以怎么用了。

五、Stream API 與 Lambda 表達式

Stream API是JDK8的一個新特性,和 Lambda 的關系在於其可以接受 Lambda 表達式參數,使代碼更加簡潔方便。 具體使用可參考 JDK 文檔:Java 8 中文版 - 在線 API 中文手冊 - 碼工具 中的Interface Stream<T>這里面有常用的map,sort等方法的接口聲明。

至於 Stream 的更詳細的知識,后續再寫。暫時只寫Lambda。

常用功能和方法說明:

 

六、參考:

  1. Lambda 表達式有何用處? - FACEBOOK THEM - CSDN 博客 注:特別優秀的一篇教程,很棒。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM