Java8 實戰(zhàn)學(xué)習(xí) 「Lambda 表達式」

Java8 實戰(zhàn)學(xué)習(xí) — Lambda 表達式

上一章趾代,我們學(xué)習(xí)了參數(shù)化代碼的實現(xiàn)方法辜腺,這個邏輯的推導(dǎo)對我自己來說還是蠻有意義的宣鄙,因為這將對我以后的代碼編輯產(chǎn)生影響番电。

這一節(jié)我們繼續(xù)學(xué)習(xí)岗屏,我們將學(xué)習(xí) Lambda 表達式的具體使用辆琅。

Lambda 概述:

可以把Lambda表達式理解為簡潔地表示可傳遞的匿名函數(shù)的一種方式:它沒有名稱,但它 有參數(shù)列表这刷、函數(shù)主體婉烟、返回類型,可能還有一個可以拋出的異常列表暇屋。這個定義夠大的似袁,讓我 們慢慢道來。

Lambda 的主要特點就是需要我們寫的東西很少咐刨,但是構(gòu)建 Lambda 的過程是需要思考的昙衅,以前看到一個接口或者一個抽象類,我們第一反應(yīng)是 new Function(){...} 定鸟,但是有了 Lambda 以后我們需要重新思考而涉,以便于我們代碼更加便于閱讀和減少代碼的書寫。


Lambda 的標準形式:

書中通過構(gòu)建一個 Comparator 對象來給我們舉例說明如果替換原來的匿名內(nèi)部類寫法联予。 我們都知道實現(xiàn)一個 Comparator 的方法:

    Comparator<Apple> byWeight = new Comparator<Apple>() {
        public int compare(Apple a1, Apple a2) {
            return a1.getWeight().compareTo(a2.getWeight());
        }
    };

我們可以使用 Comparator 的方法為:

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

這看起來并沒有難度啼县,閱讀起來是 我有兩個蘋果對象,拿參數(shù) a1,a2 作為比較對象,第一個蘋果的重量和第二個蘋果的重量比較沸久,并返回比較結(jié)果(0代表相同谭羔,大于0的數(shù)代表a1重量大于a2重量,小于0則相反)麦向。

請注意你基本上只傳遞了比較兩個蘋果重量所真 正需要的代碼】筒妫看起來就像是只傳遞了compare方法的主體诵竭。

書中給出了上述解釋,之后給出語法通用表示:

  1. (parameters) -> expression 即 (參數(shù))-> 方法實現(xiàn) 適合單個語句
  2. (parameters) -> { statements; } (參數(shù))-> {方法實現(xiàn);} 適合多語句

注意:

  1. 使用第一種方式不可出現(xiàn) 兼搏; 若方法實現(xiàn)即 Lambda 主體如果有多個語句卵慰,就必須使用{;}

  2. 書中 (String s) -> s.length() 這個例子的后邊說了一句話當時對我造成了不少困惑:

    第一個Lambda表達式具有一個String類型的參 數(shù)并返回一個 int 。 Lambda沒有 return 語句佛呻, 因為已經(jīng)隱含了return

    這說明的意思說 如果是第一種表達方式或者第二種表達方式的返回void 都屬于 return 類型可以推敲的時候裳朋,可以不寫 return ,并不能說 Lambda 沒有 return吓著。


哪里可以使用 Lambda

Lambda表達式允許你直接以內(nèi)聯(lián)的形式為函數(shù)式接口的抽象方法提供實現(xiàn)鲤嫡,并把 整個表達式作為函數(shù)式接口的實例 (具體說來,是函數(shù)式接口一個具體實現(xiàn) 的實例)绑莺。你用匿名內(nèi)部類也可以完成同樣的事情暖眼,只不過比較笨拙:需要提供一個實現(xiàn),然后 再直接內(nèi)聯(lián)將它實例化纺裁。

這里有兩個概念:

  1. 函數(shù)式接口:函數(shù)式接口就是只定義一個抽象方法的接口
  2. Lambda 是函數(shù)式接口的抽象方法的實現(xiàn)诫肠,但它可以作為整個接口的一個具體實例司澎。

厲害了我的哥,第二點難理解栋豫,但這又恰恰是讀懂 Lambda 表達式的必須需要理解的地方挤安。

舉一個栗子:

// 需要調(diào)用函數(shù)式接口的方法
public static void process(Runnable r) {
    r.run();
}

// 之前的調(diào)用方法
Runnable r1 = new Runnable() {
   public void run() {
       System.out.println("Hello World 2");
   }
};
process(r1);

// Lambda 表達方法
process(() -> System.out.println("Hello World 3"));

相信如果有之前實現(xiàn)的方法作比較,我們會很好明白丧鸯,但是如果你擋住上邊的實現(xiàn)蛤铜,去看下邊的 lambda 我相信好多人跟我一樣懵逼。這是什么鬼骡送? 雖然我知道結(jié)果會是 Hello World 3 昂羡,但是這究竟是什么?

思路是這樣的:

  1. process 方法只接受 Runnable 的具體實現(xiàn)類
  2. Lambda 可以作為函數(shù)式接口的一個實現(xiàn)實例
  3. () -> System.out.println("Hello World 3") 應(yīng)該就是 Runnable 的一個具體實現(xiàn)摔踱。
  4. Lambda 是函數(shù)式接口的抽象方法的實現(xiàn)虐先,也就是這句話同時也是 run() 方法的具體實現(xiàn),run方法不需要參數(shù)派敷,所以箭頭前邊為一個空的 ()蛹批。() -> void代表 了參數(shù)列表為空,且返回void的函數(shù)篮愉。

這充分說明 Lambda 寫得少想得多的特點腐芍。但是我們也發(fā)現(xiàn)了他的一個優(yōu)點就是「簡單易讀」,我們看到這句話第一個反應(yīng)就是:它將打印出 Hello World 3 试躏。


方法簽名

Java 方法簽名 :java 的方法簽名由 全類名.方法名(形參數(shù)據(jù)類型列表)返回值數(shù)據(jù)類型 決定,在方法存在重載的時候猪勇,方法返回值沒有什么意義,是由方法名和參數(shù)列表決定的颠蕴。

Lambda 表達式的簽名:函數(shù)式接口的抽象方法的簽名基本上就是Lambda表達式的簽名泣刹。

書中舉了個錯誤的例子:ApplePredicate<Apple> p = (Apple a) -> a.getWeight();
因為之前我們定義的時候:

public interface ApplePredicate{ boolean test (Apple apple); }

我們可以看到,函數(shù)式接口 ApplePredicate test 的方法簽名中犀被,返回值為 boolean 但是錯誤代碼中返回了int值椅您,所以方法簽名不同。


動手實現(xiàn)一個 Lambda 付諸實踐

動手練習(xí):將下列代買轉(zhuǎn)化為 Lambda 實現(xiàn):

   public static String proccessFile() throws IOException {
        try (BufferedReader br = new BufferedReader((new FileReader("data.txt")))) {
            return br.readLine();
        }
    }
  1. 行為參數(shù)化

    實際操作中我們可能需要對文件進行不同的操作寡键,比如讀取兩行掀泳,讀取最后一行。所以我們要將 proccessFile () 方法 對文件進行不同操作的行為西轩,進行參數(shù)化员舵。

    我們期待的結(jié)果是 通過 processFile 拿到文件的讀取結(jié)果,具體怎么讀取應(yīng)該由接口的抽象方法具體實現(xiàn)藕畔,所以 processFile 的行為就是如何操作文件固灵,我們需要將他參數(shù)化。

  2. 使用函數(shù)接口來傳遞行為

    為了之后我們能夠使用 Lambda 來實現(xiàn)這個功能劫流,我們需要創(chuàng)建一個 函數(shù)式接口巫玻。對于初學(xué)者來說這點應(yīng)該是最難得丛忆,我們應(yīng)該如何創(chuàng)建這個函數(shù)式接口。

    1. 明確任務(wù)條件: 讀取文件需要一個文件的讀取流 這里假定是字符流 BufferReader
    2. 明確任務(wù)結(jié)果: 返回讀取的結(jié)果仍秤,應(yīng)該是一個 String

    即 BufferReader -> String 的過程

```
interface BufferedReaderProcessor {
        String process(BufferedReader bufferedReader);
}
```
  1. 執(zhí)行一個行為
    執(zhí)行這個行為將需要用到 processFile 方法了熄诡,該方法參數(shù)為函數(shù)式接口 BufferedReaderProcessor ,用該接口的是實例來進行文件操作诗力,具體怎么操作由該實例決定:

      public static String processFile(BufferedReaderProcessor p) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
            return p.process(br);
        }
    }
    
  1. 改為 Lambda 形式完成 process 的具體實現(xiàn)

    通過之前的學(xué)習(xí)凰浮,我們知道了一個重要的概念就是 Lambda 可以作為,Lambda 是函數(shù)式接口的抽象方法的實現(xiàn)苇本,但它可以作為整個接口的一個具體實例袜茧。

    1. 需要實現(xiàn)這個接口的具體方法
    2. 整體作為實例傳遞給 processFile

    假設(shè)我們現(xiàn)在需要完成一讀取文章前兩行的操作:

    1. (parameters) -> expression

      (BufferReader br) -> br.readLine()+br.readLine();
      
    2. (parameters) -> { statements; }

       String readline = proccessFile((BufferedReader br) -> {
           //readLine 方法會有警告 瓣窄,之后會學(xué)習(xí)如何處理 lambda 中的警告 這里重點在于 lambda 的實現(xiàn)笛厦。
                      String line1 = br.readLine();
                      String line2 = br.readLine();
                      return line1 + line2;
                  });
      
      

類型檢查

Lambda 可以作為函數(shù)式接口生成一個實例。然而俺夕,Lambda 表達式本身并不包含它在實現(xiàn)哪個函數(shù)式接口的信息

正如我們文章開頭提到的那樣裳凸,Lambda 本身并沒有什么意義,只有結(jié)合上下文劝贸,更通俗的說是等號左邊的內(nèi)容姨谷,才是我們最終想要的 函數(shù)式接口具體實現(xiàn) 對象。

Lambda的類型是從使用Lambda的上下文推斷出來的映九。

這里的上下文包括: 方法簽名(參數(shù)梦湘,返回值),以及使用這個 Lambda 的方法的參數(shù)類型件甥。

List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);

這個例子還是延續(xù)第一篇中的那個篩選蘋果的例子捌议,書中給出了很好的解讀這里就不多贅述直接上圖:

看到這里我只能說這本書翻譯的太好了。

依次類推我們看地方代碼的時候如果它使用了 Lambda 方式書寫代碼嚼蚀,我們可以通過以下方式來找到這個 Lambda 的含義:

  1. 找到使用 Lambda 函數(shù)式參數(shù)方法的方法定義。
  2. 查看Lambda 所需要實例化的抽象接口的內(nèi)唯一的一個抽象方法的方法簽名(這里是 T -> boolen)
  3. 檢查 Lambda 表達式是否滿足這個抽象方法的方法簽名即可管挟。

聰明的人可能發(fā)現(xiàn)了貓膩轿曙,我們可能好多的函數(shù)式接口的抽象方法的方法簽名都是 T -> boolen,那么 Lambda 的使用會不會出現(xiàn)問題 ?

事實上僻孝,同一個 Lambda 表達式导帝,如果 Lambda 表達式的方法簽名 = 函數(shù)式接口的抽象方法接口 那么這個這個 Lambda 就是有效的。

這就需要在我們書寫或者閱讀的時候必須結(jié)合上下文穿铆,而 JVM 需要做的事情更多您单,但具體原理并不影響我們使用,所以就先不探討荞雏。


類型推斷

之前說過 Lambda 表達式還可以繼續(xù)簡單化代碼虐秦,剛才我們學(xué)習(xí)了類型檢查平酿,相同的 Lambda 表達式可以匹配不同的函數(shù)接口,JVM 可以根據(jù)我們使用 Lambda 表達式的上下文來決定匹配什么樣的函數(shù)接口來接收此表達式悦陋。

如果這個 Lambda 的目標類型是可以推斷出來的蜈彼,而參數(shù)類型也只有一種的時候,我們可以省略參數(shù)的類型:

List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));

Lambda表達式有多個參數(shù)俺驶,代碼可讀性的好處就更為明顯,以下兩個方式是等價的幸逆。

Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());

書中也給了我們提醒,有的時候沒有類型推斷的 Lambda 表達式更易讀暮现,有時候去掉看起來更好还绘。
需要我們自己去選擇。


至此我們已經(jīng)成功完成了一次lambda 的實踐過程栖袋。通過學(xué)習(xí)我的體會就是想要 lambda 的學(xué)習(xí)還是需要多加練習(xí)拍顷。否則總會處在放下課本就忘得狀態(tài)。至此我們學(xué)習(xí)完了栋荸,課本的3.3章的內(nèi)容菇怀。而之后將會介紹一個新的概念叫「方法引用」,它將會使代碼更加簡潔晌块,但是同時也帶來了更大的挑戰(zhàn)爱沟,一起來加油吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匆背,一起剝皮案震驚了整個濱河市呼伸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钝尸,老刑警劉巖括享,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異珍促,居然都是意外死亡铃辖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門猪叙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娇斩,“玉大人,你說我怎么就攤上這事穴翩∪冢” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵芒帕,是天一觀的道長歉嗓。 經(jīng)常有香客問我,道長背蟆,這世上最難降的妖魔是什么鉴分? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任哮幢,我火速辦了婚禮,結(jié)果婚禮上冠场,老公的妹妹穿的比我還像新娘家浇。我一直安慰自己,他們只是感情好碴裙,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布钢悲。 她就那樣靜靜地躺著,像睡著了一般舔株。 火紅的嫁衣襯著肌膚如雪莺琳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天载慈,我揣著相機與錄音惭等,去河邊找鬼。 笑死办铡,一個胖子當著我的面吹牛辞做,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寡具,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼秤茅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了童叠?” 一聲冷哼從身側(cè)響起框喳,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厦坛,沒想到半個月后五垮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡杜秸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年放仗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撬碟。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡诞挨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出小作,到底是詐尸還是另有隱情亭姥,我是刑警寧澤稼钩,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布顾稀,位于F島的核電站,受9級特大地震影響坝撑,放射性物質(zhì)發(fā)生泄漏静秆。R本人自食惡果不足惜粮揉,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抚笔。 院中可真熱鬧扶认,春花似錦、人聲如沸殊橙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膨蛮。三九已至叠纹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敞葛,已是汗流浹背誉察。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惹谐,地道東北人持偏。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像氨肌,于是被迫代替她去往敵國和親鸿秆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容