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