Java 8 和 Scala 中的 Lambda 表示式
Java8 終於要支援Lambda表示式!自2009年以來Lambda表示式已經在Lambda專案中被支援。在那時候,Lambda表示式仍被稱為Java閉包。在我們進入一些程式碼示例以前,先來解釋下為什麼Lambda表示式在Java程式員中廣受歡迎。
1、為什麼使用Lambda表示式
Lambda表示式通常使用在圖形使用者介面(GUI)的開發中。一般來說,GUI程式設計將程式行為和事件做連線。比如,當用戶按下一個按鈕(觸發一個事件),你的程式就需要去執行某些行為,可能是將一些資料儲存到一個數據儲存器中。在Swing中,可以使用ActionListener來實現:
class ButtonHandler implements ActionListener { public void actionPerformed(ActionEvent e) { //do something } } class UIBuilder { public UIBuilder() { button.addActionListener(new ButtonHandler()); } }
這個例子表明了 ButtonHandler 類作為一個回撥替換的用法。在這裡 ButtonHandler 類僅包含 ActionListener 介面定義的 actionPerformed 方法。我們可以使用匿名內部類來簡化程式碼:
class UIBuilder { public UIBuilder() { button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { //do something } }) } }
這樣程式碼簡潔多了。更仔細的去看程式碼時,就會發現我們還建立一個只生成一個例項的類,而這個例項也僅僅持有一個獨立的方法。這恰好是Lambda表示式所能解決的其中一類問題。
2、Lambda表示式代替函式
一個lambda表示式從字面上講就是一個函式。它定義了一個函式的輸入引數和函式體。Java 8 中的,lambda表示式語法尚未確定,不過大致應該類似這個樣子的:
(type parameter) -> function_body
一個具體的例子:
(String s1, String s2) -> s1.length() - s2.length();
這個lambda表示式用來計算兩個字串的長度差。還有一些擴充套件的語法,比如避免參數的型別定義(我們馬上見看到例子)還有使用{和}來支援多行定義。
Collections.sort() 方法是lambda表達的理想例子。它允許我們將字串按照長度排序:
List<String> list = Array.asList("loooooong", "short", "tiny"); Collections.sort(list, (String s1, String s2) -> s1.length() - s2.length()); > "tiny", "short", "loooooong".
所以,不像現在java必須要求的向sort方法輸入一個已經實現的Comparator(比較器)而是傳送一個lambda表示式我們就可以得到相同的結果。
3、Lambda表示式代替閉包
lambda表示式有許多有趣的特性。其中之一是,它們是閉包。一個閉包允許函式訪問直接詞法作用域之外的變數。
String outer = "java 8" (String s1) -> s1.length() - outer.length()
在例子中,lambda表示式訪問了字串 outer 這個作用域之外定義的變數。對於內聯閉包來說這是很難做到的。
4、Lambda表示式也支援型別推論
型別推論是java 7 引入的但它同樣適用於lambda表示式。簡單來說,型別推論意味著程式設計師可以在任意一個編譯器能夠自動推斷出型別的地方省略型別定義。
如果型別推論能夠應用到前面的排序lambda表示式上,那麼它就能寫成下面的樣子:
List<String> list = Arrays.asList(...); Collections.sort(list, (s1, s2) -> s1.length()-s2.length());
就像你所見到的一樣,引數s1和s2的型別被省略了。因為編譯器知道list是一個字串集合,它知道被用來作為比較器的lambda表示式必定是相同的型別。因此,這個型別不需要顯式地宣告,即使你有這麼做的自由。
型別推論的主要優勢就是減少樣板程式碼,如果編譯器可以為我們識別型別,為什麼我們必須自己定義它們。
5、珍愛Lambda表示式,遠離匿名內部類
我們來體會下,為何lambda表示式和型別推論有助於簡化我們前面所提到的回撥例子:
class UIBuilder { public UIBuilder() { button.addActionListener(e -> //process ActionEvent e) } }
我們下載直接傳送一個lambda表示式進入 addActionListener 方法來代替前面定義的持有回撥方法的類。除了減少模板程式碼和提高可讀性以外,它使我們直接表達我們唯一感興趣的事情:處理事件。
在我們瞭解lambda表示式更多優勢之前,先來看看在Scala中的lambda表示式副本。
6、Scala中的Lambda表示式
在函數語言程式設計中,函式是基本的構造塊。Scala融合了java中的面向物件程式設計和函數語言程式設計。在Scala中,一個lambda表示式是種叫做“函式”或者“函式文字”。Scala中的函式屬於一等公民。它們可以被分配給vals或者vars(最終變數或者非最終變數),它們可以作為其他函式的引數,也可以組合成新的函式。
在Scala中一個函式文字寫成如下形式:
(argument) => //funtion body
舉例來說,前面提到的java 用來計算兩個字串長度差的 lambda 表示式,在Scala中寫作如下:
(s1: String, s2 :String) => s1.length - s2.length
Scala中的函式文字也是閉包。它可以訪問在直接詞法作用域之外定義的變數。
val outer =10 val myFuncLiteral = (y: Int) => y * outer val result = myFuncLiteral(2) > 20
這個例子結果是20.
正如你所見,我們將函式文字分配給了變數 myFuncLiteral。
java 8 的lambda表示式和Scala的函式文字在語法和語義上的相似性是十分明顯的。從語義上講它們是相同的,而語法上的唯一不同就是箭頭符號(java8 ->, scala =>)和我們沒有提到的簡化符號。
本文來自雲棲社群合作伙伴“開源中國”
本文作者:散裝海盜
ofollow,noindex">原文連結