一、lambda含義
lambda表示數學符號“λ”,電腦領域中λ代表“λ演算”,表達了電腦中最基本的概念:“調用”和“置換”。在很多動態語言和C#中都有相應的lambda文法,這類文法都為了簡化代碼,提高運行效率。
二、lambda 項目的背景,參考這裡。
無論是物件導向語言還是函數式語言,基本數值都可以被動態封裝入程式動作:物件導向語言通過“方法”,函數式語言通過“函數。
介於“方法”和“函數”的定義有很多種,補充下IBM知識庫的解釋:
在物件導向語言中,方法不是一階值(First-class value),在函數式語言中,函數是一階值。在函數式語言中,函數可以作為另一個函數的傳回值或參數,還可以作為一個變數的值,函數可以嵌套定義,而在物件導向語言中的的“方法”做不到這點。
Java可以說是物件導向語言的代表,如果要調用其方法,需要先建立對象。不過Java對象都是“重量級”的,執行個體化具體的類的對象,需要經過定義和申明兩個階段。比如定義方法,並給內部欄位賦初始值。但是一個對象只包含一個方法的情況很多,比如實現API中的“回調介面”功能的類,在swing中有介面:
Java代碼 <span><span><span style="">public interface ActionListener { void actionPerformed(ActionEvent e); }</span></span></span>
現有的實現方式大多是:
Java代碼 <span><span><span style="">button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ui.dazzle(e.getModifiers()); } });</span></span></span>
很多現有的類庫都基於這種設計實現,所以對於代碼被明確定義運行在單獨線程的API來說,匿名內部類尤為重要。這些匿名內部類只存在於建立它的線程中。但是在並行計算領域,CPU的製造商著力發展多核技術來提升CPU的功能,這麼做幾乎無法依靠多核的優勢來提升其效能。
鑒於回呼函數和其他功能式文法的關係越來越密切,所以必須建立儘可能的輕量級的資料模型(從編碼角度而言,效能方面下文再說)。對於這點來說匿名內部類的缺點如下:
1. 文法相對複雜。
2. 在調用內部類的上下文中,指引和this的指代容易混淆。
3. 類載入和執行個體建立文法不可避免。
4. 不能引用外部的非final對象。
5. 不能抽象化控制流程程
針對這些問題,lambda項目致力於
1. 消除問題1和問題2,通過引入更簡單的運算式和局部變數的定義規則。
2. 迴避問題3,定義更靈活更友善的文法。這裡只是迴避,類載入和執行個體化本身不可避免。下文會解釋。
3. 改善問題4,允許使用者使用最終有效局部變數。
不過lambda項目目前並不能解決所有關於內部類的問題。問題4和問題5沒有完全解決,這計劃在將類版本中繼續改善。對於效能方面,原文也沒有提,不過後面有些補充。
三、lambda用法
通過上文可以瞭解到,lambda文法是針對“回調介面”和“匿名內部類”作出的改進,所以lambda的文法目前僅對於部分介面,這些介面的特點是只含一個抽象方法,在lambda項目中,早期稱為SAM類型(SAM = single abstract method 即單一抽象方法)。在最新的文檔中(即這個版本),它們有了新名字,叫函數介面(functional interface),比如:
1 java.lang.Runnable 2 java.util.concurrent.Callable 3 java.security.PrivilegedAction 4 java.util.Comparator 5 java.io.FileFilter 6 java.nio.file.PathMatcher 7 java.lang.reflect.InvocationHandler 8 java.beans.PropertyChangeListener 9 java.awt.event.ActionListener 10 javax.swing.event.ChangeListener
lambda的文法包括三部分 1、參數列表 2、箭頭符號"->" 3、代碼塊。
其中代碼塊很像一個方法體,return語句將控制權交還給匿名方法(anonymous method,即lambda運算式)的調用者;break和continue不能出現在函數體的頂部,不過可以出現在內部的迴圈裡;如果代碼塊得出最終結果,那麼每一個控制路徑(control path) 必須都有返回或拋出異常。
如果代碼塊只有簡單一行,可以省略return關鍵字和“{}”符號(以下所寫的例子都是基於JDK 1.8 lambda預覽版),比如:
Java代碼 <span><span><span style="">public class LambdaTest { public static void main(String... args) { //這裡有{}和return 以及 ; Runnable r = () -> { System.out.println("hello world"); }; //這裡不需要{}和return java.util.Comparator<String> c = (String s1, String s2) -> s2.length()-s1.length(); r.run(); System.out.println(c.compare("s1", "12323")); } }</span></span></span>
輸出為:
hello world
3
除了這些現有介面,我們還可以自訂函數介面:
Java代碼 <span><span><span style="">public class LambdaTest { interface lambdaInterface { public void me(String str); } public static void main(String... args) { lambdaInterface li = (String s)->{System.out.println(s);}; li.me("hello world!"); } }</span></span></span>
輸出為:
hello world!
新的lambda方法從文法上的確是簡化了很多。和lambda第一次發布的文法相比也優雅很多。
四、lambda代碼塊的位元組碼
看完了文法的確很簡單,那麼lambda是怎麼實現的,就得從位元組碼考察了。這裡和匿名內部類做個對比,編譯如下代碼:
Java代碼 <span><span><span style="">public class LambdaTest