前言
最近幾年lambda表達(dá)式風(fēng)靡于編程界。很多現(xiàn)代編程語言都把它作為函數(shù)式編程的基本組成部分。基于jvm的編程語言如scala、groovy及clojure把它作為關(guān)鍵部分集成在語言中。而如今,(最終)java 8也加入了這個有趣的行列。
java8 終于要支持lambda表達(dá)式!自2009年以來lambda表達(dá)式已經(jīng)在lambda項目中被支持。在那時候,lambda表達(dá)式仍被稱為java閉包。在我們進(jìn)入一些代碼示例以前,先來解釋下為什么lambda表達(dá)式在java程序員中廣受歡迎。
1、為什么使用lambda表達(dá)式
lambda表達(dá)式通常使用在圖形用戶界面(gui)的開發(fā)中。一般來說,gui編程將程序行為和事件做連接。比如,當(dāng)用戶按下一個按鈕(觸發(fā)一個事件),你的程序就需要去執(zhí)行某些行為,可能是將一些數(shù)據(jù)儲存到一個數(shù)據(jù)存儲器中。在swing中,可以使用actionlistener來實(shí)現(xiàn):
1
2
3
4
5
6
7
8
9
10
11
|
class buttonhandler implements actionlistener { public void actionperformed(actionevent e) { //do something } } class uibuilder { public uibuilder() { button.addactionlistener( new buttonhandler()); } } |
這個例子表明了 buttonhandler 類作為一個回調(diào)替換的用法。在這里 buttonhandler 類僅包含 actionlistener 接口定義的 actionperformed 方法。我們可以使用匿名內(nèi)部類來簡化代碼:
1
2
3
4
5
6
7
8
9
|
class uibuilder { public uibuilder() { button.addactionlistener( new actionlistener() { public void actionperformed(actionevent event) { //do something } }) } } |
這樣代碼簡潔多了。更仔細(xì)的去看代碼時,就會發(fā)現(xiàn)我們還創(chuàng)建一個只生成一個實(shí)例的類,而這個實(shí)例也僅僅持有一個獨(dú)立的方法。這恰好是lambda表達(dá)式所能解決的其中一類問題。
2、lambda表達(dá)式代替函數(shù)
一個lambda表達(dá)式從字面上講就是一個函數(shù)。它定義了一個函數(shù)的輸入?yún)?shù)和函數(shù)體。java 8 中的,lambda表達(dá)式語法尚未確定,不過大致應(yīng)該類似這個樣子的:
1
|
(type parameter) -> function_body |
一個具體的例子:
1
|
(string s1, string s2) -> s1.length() - s2.length(); |
這個lambda表達(dá)式用來計算兩個字符串的長度差。還有一些擴(kuò)展的語法,比如避免參數(shù)的類型定義(我們馬上見看到例子)還有使用{和}來支持多行定義。
collections.sort()
方法是lambda表達(dá)的理想例子。它允許我們將字符串按照長度排序:
1
2
3
|
list<string> list = array.aslist( "loooooong" , "short" , "tiny" ); collections.sort(list, (string s1, string s2) -> s1.length() - s2.length()); > "tiny" , "short" , "loooooong" . |
所以,不像現(xiàn)在java必須要求的向sort方法輸入一個已經(jīng)實(shí)現(xiàn)的comparator(比較器)而是傳送一個lambda表達(dá)式我們就可以得到相同的結(jié)果。
3、lambda表達(dá)式代替閉包
lambda表達(dá)式有許多有趣的特性。其中之一是,它們是閉包。一個閉包允許函數(shù)訪問直接詞法作用域之外的變量。
1
2
|
string outer = "java 8" (string s1) -> s1.length() - outer.length() |
在例子中,lambda表達(dá)式訪問了字符串 outer 這個作用域之外定義的變量。對于內(nèi)聯(lián)閉包來說這是很難做到的。
4、lambda表達(dá)式也支持類型推論
類型推論是java 7 引入的但它同樣適用于lambda表達(dá)式。簡單來說,類型推論意味著程序員可以在任意一個編譯器能夠自動推斷出類型的地方省略類型定義。
如果類型推論能夠應(yīng)用到前面的排序lambda表達(dá)式上,那么它就能寫成下面的樣子:
1
2
|
list<string> list = arrays.aslist(...); collections.sort(list, (s1, s2) -> s1.length()-s2.length()); |
就像你所見到的一樣,參數(shù)s1和s2的類型被省略了。因為編譯器知道list是一個字符串集合,它知道被用來作為比較器的lambda表達(dá)式必定是相同的類型。因此,這個類型不需要顯式地聲明,即使你有這么做的自由。
類型推論的主要優(yōu)勢就是減少樣板代碼,如果編譯器可以為我們識別類型,為什么我們必須自己定義它們。
5、珍愛lambda表達(dá)式,遠(yuǎn)離匿名內(nèi)部類
我們來體會下,為何lambda表達(dá)式和類型推論有助于簡化我們前面所提到的回調(diào)例子:
1
2
3
4
5
|
class uibuilder { public uibuilder() { button.addactionlistener(e -> //process actionevent e) } } |
我們下載直接傳送一個lambda表達(dá)式進(jìn)入 addactionlistener 方法來代替前面定義的持有回調(diào)方法的類。除了減少模板代碼和提高可讀性以外,它使我們直接表達(dá)我們唯一感興趣的事情:處理事件。
在我們了解lambda表達(dá)式更多優(yōu)勢之前,先來看看在scala中的lambda表達(dá)式副本。
6、scala中的lambda表達(dá)式
在函數(shù)式編程中,函數(shù)是基本的構(gòu)造塊。scala融合了java中的面向?qū)ο缶幊毯秃瘮?shù)式編程。在scala中,一個lambda表達(dá)式是種叫做“函數(shù)”或者“函數(shù)文本”。scala中的函數(shù)屬于一等公民。它們可以被分配給vals或者vars(最終變量或者非最終變量),它們可以作為其他函數(shù)的參數(shù),也可以組合成新的函數(shù)。
在scala中一個函數(shù)文本寫成如下形式:
1
|
(argument) => //funtion body |
舉例來說,前面提到的java 用來計算兩個字符串長度差的 lambda 表達(dá)式,在scala中寫作如下:
1
|
(s1: string, s2 :string) => s1.length - s2.length |
scala中的函數(shù)文本也是閉包。它可以訪問在直接詞法作用域之外定義的變量。
1
2
3
4
|
val outer = 10 val myfuncliteral = (y: int ) => y * outer val result = myfuncliteral( 2 ) > 20 |
這個例子結(jié)果是20.
正如你所見,我們將函數(shù)文本分配給了變量 myfuncliteral。
java 8 的lambda表達(dá)式和scala的函數(shù)文本在語法和語義上的相似性是十分明顯的。從語義上講它們是相同的,而語法上的唯一不同就是箭頭符號(java8 ->, scala =>)和我們沒有提到的簡化符號。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://yq.aliyun.com/articles/657841