Java 8 和 Scala 中的 Lambda 表達式 – JAVA編程語言程序開發技術文章

Java8 終於要支持Lambda表達式!自2009年以來Lambda表達式已經在Lambda項目中被支持。在那時候,Lambda表達式仍被稱為Java閉包。在我們進入一些代碼示例以前,先來解釋下為什麼Lambda表達式在Java程序員中廣受歡迎。
1、為什麼使用Lambda表達式
Lambda表達式通常使用在圖形用戶界面(GUI)的開發中。一般來說,GUI編程將程序行為和事件做連接。比如,如果用戶按下一個按鈕(除法一個事件),你的程序就需要去執行某些行為,可能是將一些數據儲存到一個數據存儲中。在Swing中,可以使用ActionListener來實現:
01
class ButtonHandler implements ActionListener {
02
    public void actionPerformed(ActionEvent e) {
03
        //do something
04
    }
05
}
06
 
07
class UIBuilder {
08
    public UIBuilder() {
09
        button.addActionListener(new ButtonHandler());
10
    }
11
}

這個例子表明瞭 ButtonHandler 類作為一個回調替換的用法。在這裡 ButtonHandler 類僅包含 ActionListener 接口定義的 actionPerformed 方法。我們可以使用匿名內部類來簡化代碼:
1
class UIBuilder {
2
    public UIBuilder() {
3
        button.addActionListener(new ActionListener() {
4
            public void actionPerformed(ActionEvent event) {
5
                //do something
6
            }
7
        })
8
    }
9
}

這樣代碼簡潔多瞭。更仔細的去看代碼時,就會發現我們還創建一個隻生成一個實例的類,而這個實例也僅僅持有一個獨立的方法。這恰好是Lambda表達式所能解決的其中一類問題。
2、Lambda表達式代替函數
一個lambda表達式從字面上講就是一個函數。它定義瞭一個函數的輸入參數和函數體。Java 8 中的,lambda表達式語法尚未確定,不過大致應該類似這個樣子的:
1
(type parameter) -> function_body

一個具體的例子:
1
(String s1, String s2) -> s1.length() – s2.length();

這個lambda表達式用來計算兩個字符串的長度差。還有一些擴展的語法,比如避免參數的類型定義(我們馬上見看到例子)還有使用{和}來支持多行定義。
Collections.sort() 方法是lambda表達的理想例子。它允許我們將字符串按照長度排序:
1
List<String> list = Array.asList("loooooong", "short", "tiny");
2
Collections.sort(list, (String s1, String s2) -> s1.length() – s2.length());
3
> "tiny", "short", "loooooong".

所以,不像現在java必須要求的向sort方法輸入一個已經實現的Comparator(比較器)而是傳送一個lambda表達式我們就可以得到相同的結果。
3、Lambda表達式代替閉包
lambda表達式有許多有趣的特性。其中之一是,它們是閉包。一個閉包允許函數訪問直接詞法作用域之外的變量。
1
String outer = "java 8"
2
(String s1) -> s1.length() – outer.length()

在例子中,lambda表達式訪問瞭字符串 outer 這個作用域之外定義的變量。對於內聯閉包來說這是很難做到的。
4、Lambda表達式也支持類型推論
類型推論是java 7 引入的但它同樣適用於lambda表達式。簡單來說,類型推論意味著程序員可以在任意一個編譯器能夠自動推斷出類型的地方省略類型定義。
如果類型推論能夠應用到前面的排序lambda表達式上,那麼它就能寫成下面的樣子:
1
List<String> list = Arrays.asList(…);
2
Collections.sort(list, (s1, s2) -> s1.length()-s2.length());

就像你所見到的一樣,參數s1和s2的類型被省略瞭。因為編譯器知道list是一個字符串集合,它知道被用來作為比較器的lambda表達式必定是相同的類型。因此,這個類型不需要顯式地聲明,即使你有這麼做的自由。
類型推論的主要優勢就是減少樣板代碼,如果編譯器可以為我們識別類型,為什麼我們必須自己定義它們。
5、珍愛Lambda表達式,遠離匿名內部類
我們來體會下,為何lambda表達式和類型推論有助於簡化我們前面所提到的回調例子:
1
class UIBuilder {
2
    public UIBuilder() {
3
        button.addActionListener(e -> //process ActionEvent e)
4
    }
5
}

我們下載直接傳送一個lambda表達式進入 addActionListener 方法來代替前面定義的持有回調方法的類。除瞭減少模板代碼和提高可讀性以外,它使我們直接表達我們唯一感興趣的事情:處理事件。
在我們瞭解lambda表達式更多優勢之前,先來看看在Scala中的lambda表達式副本。
6、Scala中的Lambda表達式
在函數式編程中,函數是基本的構造塊。Scala融合瞭java中的面向對象編程和函數式編程。在Scala中,一個lambda表達式是種叫做“函數”或者“函數文本”。Scala中的函數屬於一等公民。它們可以被分配給vals或者vars(最終變量或者非最終變量),它們可以作為其他函數的參數,也可以組合成新的函數。
在Scala中一個函數文本寫成如下形式:
1
(argument) => //funtion body

舉例來說,前面提到的java 用來計算兩個字符串長度差的 lambda 表達式,在Scala中寫作如下:
1
(s1: String, s2 :String) => s1.length – s2.length

Scala中的函數文本也是閉包。它可以訪問在直接詞法作用域之外定義的變量。
1
val outer =10
2
val myFuncLiteral = (y: Int) => y * outer
3
val result = myFuncLiteral(2)
4
> 20

這個例子結果是20.
正如你所見,我們將函數文本分配給瞭變量 myFuncLiteral。
java 8 的lambda表達式和Scala的函數文本在語法和語義上的相似性是十分明顯的。從語義上講它們是相同的,而語法上的唯一不同就是箭頭符號(java8 ->, scala =>)和我們沒有提到的簡化符號。

作者:散裝強盜

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *