Java 8中,最重要的一個改變使得代碼更快、更簡潔,并向FP(函數式編程)打開了方便之門。下面我們來看看,它是如何做到的。
在上世紀九十年代,Java被設計成了OOP語言,在當時,OOP是軟件開發中的標桿。遠在OOP還沒有出現的時候,已經產生了FP語言,例如Lisp和Scheme,但是它們的益處,并沒有受到學術圈外的人重視。最近,FP的重要性被提升了,因為它非常適合并發編程和事件驅動編程。然而,這并不意味著OO不好,相反,好的策略應該是混用OOP和FP。就算你對并發編程不感興趣,這也很有道理。例如,如果編程語言有一個方便寫函數表達式的語法,集合類庫就能擁有強大的API。
Java 8中最主要的增強,就是把FP的概念深度整合進OO。在本文中,我將會展示其基本語法以及,在不同的上下文中,如何使用它。關鍵點如下:
- Lambda表達式,就是有參數的代碼塊。
- 在任何時候,你想稍后執行一個代碼塊時,用Lambda表達式。
- Lambda表達式可以被轉型成函數式接口。
- Lambda表達式可以從封閉作用域有效訪問final的變量。
- 方法和構造器的引用可在不調用它們的情況下引用它們。
- 你現在可以把default和static方法的具體實現寫在接口中。
- 你必須手動解決不同接口中任何的default方法沖突。
為什么需要Lambda表達式?
Lambda表達式是一個代碼塊,你可以繞過它,因此它能在稍后執行,僅一次或多次。在介紹語法(甚至是奇怪的名稱)之前,讓我們后退一步,看看一直以來,你在Java中,類似的代碼塊會在什么地方用到。
當你想在一個獨立的線程中執行代碼時,你把代碼放到Runnable的run方法中,就像這樣:
-
class Worker implements Runnable {
-
public void run() {
-
for (int i = 0; i < 1000; i++)
-
doWork();
-
}
-
// …
-
}
然后,當你想執行這段代碼時,你創建一個Worker實例,把它提交給線程池,或者簡單的開始一個新線程:
-
Worker w = new Worker();
-
new Thread(w).start();
這里的關鍵點在于,run方法中包含你想在獨立線程中執行的代碼。
想想用自定義的Comparator排序。如果你想以長度,而不以默認的字典順序對字符串排序,你可以傳遞一個Comparator對象給sort方法:
-
class LengthComparator implements Comparator< String > {
-
public int compare(String first, String second) {
-
return Integer.compare(first.length(), second.length());
-
}
-
}
-
-
Arrays.sort(strings, new LengthComparator());
sort方法會持續調用compare方法,重排亂序的元素,直到數組排序完畢。你給sort方法傳遞一個比較元素的代碼片段,這段代碼被整合進其余的、你也許不想重新去實現的排序邏輯。注意:如果 x等于y,Integer.compare(x, y)返回0;x < y,返回負數;x > y,返回正數。這個static方法在Java 7中被加入。你千萬不能計算x - y來比較它們的大小,那樣符號相反的大操作數會導致計算溢出的。
作為另外一個延后執行的例子,考慮一個按鈕回調。你新建一個繼承Listener接口的類,把回調動作放進其中,創建它的一個實例,最后把實例注冊到按鈕。這種場景司空見慣,以至于很多程序員都使用“匿名類的匿名實例”語法:
-
button.setOnAction(new EventHandler< ActionEvent >() {
-
public void handle(ActionEvent event) {
-
System.out.println("Thanks for clicking!");
-
}
-
});
重要的是handle方法中的代碼,任何時候按鈕被點擊,它就會被執行。
因為Java 8把JavaFX作為Swing GUI工具包的繼任者,我在例子里是使用JavaFX。這些細節并不重要,因為在所有的UI工具包中,不管是Swing,JavaFX,還是Android,都是你給按鈕一些代碼,在按鈕被點擊的時候執行。
在上面的三個例子中,你看到了相同的方式。代碼塊被傳遞給某人:線程池,sort方法或按鈕,它將在稍后被調用。
到現在為止,在Java中傳遞代碼塊并不簡單。你不能只是傳遞代碼塊,Java是一個OOP語言,因此你必須先創建一個屬于某個類的實例,而這個類擁有我們需要傳遞的代碼塊。
在其他語言中,是可能直接使用代碼塊的。在很長一段時間里,Java的設計者們反對增加這個特性,畢竟,Java的偉大力量在于簡單性和一致性。如果一個語言,包含所有的能夠產生少量更緊湊代碼的特性,它就會變成不可維護的一團糟。經管如此,在其他語言中,它們并不僅僅是可以更簡單的啟動一個線程,或者注冊一個按鈕點擊的處理程序;它們中的大量API都更加簡單,更加一致,更加強大。在Java里,人們本應該能夠寫出類似的API,它們使用繼承特定函數的類的實例,但是,這樣的API,讓人用起來并不愉快。
一段時間以來,問題變成,并不是要不要在Java中添加FP,而是怎么去添加。在符合Java的設計出來之前,設計者們花了幾年的時間來做試驗。在本文下一部分,你將會看到,你是如何在Java 8中使用代碼塊的。
Lambda表達式的語法
再想想上面排序的例子。我們傳遞比較字符串長度的代碼。我們計算:
-
Integer.compare(first.length(), second.length())
fisrt和second是什么?它們都是字符串!Java是強類型語言,我們也必須指明這一點:
-
(String first, String second) -> Integer.compare(first.length(), second.length())
你看到了你的第一個Lambda表達式。它只是簡單的代碼塊和必須傳給它的變量說明。
它的名稱Lambda是怎么來的呢?很多年前,還在計算機出現之前,邏輯學家Alonzo Church想要形式化數學函數,讓它具有更有效的可計算性。(奇怪的事,人們知道有些函數的存在,卻沒有人知道怎么計算它們的值。)他用希臘字母lambda(λ)來表示函數參數。如果他懂得Jav API,他可能會這樣寫:
-
λfirst.λsecond.Integer.compare(first.length(), second.length())
那為什么是字母λ呢?是Church用完了其他所有的字母了嗎?實際上,偉大的《數學原理》使用ˆ來表示自由變量,它激發了Church用一個大寫的lambda(λ)表示參數的靈感。但是最終,他還是使用了小寫版本。自此以后,一個擁有參數的表達式就被稱為了“Lambda表達式”。
本文譯自:Lambda Expressions in Java 8
原創文章,轉載請注明: 轉載自LetsCoding.cn
本文鏈接地址: Java 8:Lambda表達式(一)