前面介紹了匿名內部類的簡單用法,經過在sort方法中運用匿名內部類,不但可以簡化代碼數量,還能保持業務代碼的連續性。只是匿名內部類的結構仍顯囉嗦,雖然它省去了內部類的名稱,可是花括號裏面的方法定義代碼一字不落,依然生生佔據了好幾行代碼。好比下面排序方法的調用代碼例子:html
Integer[] intArray = { 89, 3, 67, 12, 45 }; // 匿名內部類無需專門定義形態完整的類,只需指明新建立的實例從哪一個接口擴展而來 Arrays.sort(intArray, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o2, o1); // 倒過來的參數順序變成了降序 } });
儘管這種匿名內部類的代碼有點彆扭,然而在早期的Java編程中也只能如此了,畢竟還得按照面向對象的代碼規矩來,不然缺胳膊斷腿的匿名內部類,編譯器怎知它是什麼玩意?直到Java8推出了Lambda表達式,才迎來了匿名內部類代碼優化的曙光。
Lambda表達式實際上是一個匿名方法,所謂匿名方法指的是:它是個沒有名字的方法,但方法體的內部代碼是完整的。但是常規的方法調用都必須指定方法名稱,假如匿名方法不存在方法名稱,那麼別的地方要怎樣才能調用它呢?爲了保證編譯器可以識別匿名方法的真身,Java對它的調用時機規定了如下限制條件:
一、調用匿名方法的地方,自己必須知曉該位置的參數類型。舉個例子,Math庫的對數函數log,根據方法定義可知,它的輸入參數是雙精度類型,則程序員書寫「Math.log(1)」的時候,雖然這個1看不出數值類型,編譯器也會自動將它轉換爲雙精度數。
二、參數類型必須是某個接口,而且該接口僅聲明瞭一個抽象方法。因爲Java體系裏的方法參數要麼是基本變量類型如int、double,要麼是某個類或某個接口,就是不支持把方法做爲參數類型,所以須要藉助接口把某個方法單獨包裝一下,這樣每當給這個接口建立匿名內部類的時候,編譯器便知道接下來只能且一定調用該接口的惟一方法。
根據以上的兩個行規,對比排序方法sort可知該方法知足第一項條件,同時排序比較器Comparator也知足第二項條件,因而調用sort方法出現的匿名內部類徹底支持改寫爲Lambda表達式。一方面,由於擁有兩個參數的sort方法早已聲明第二個參數是Comparator類型,因此匿名內部類當中的該接口名稱容許略去;另外一方面,由於比較器Comparator只有惟一的抽象方法compare,因此匿名內部類裏面的方法名稱也容許略去。如此一來,既省略接口名又省略方法名的Lambda排序代碼示例以下:java
// Lambda表達式第一招。去掉了new、接口名稱、方法名稱 Arrays.sort(intArray, (Integer o1, Integer o2) -> { return Integer.compare(o2, o1); // 按照降序排列 });
仔細觀察上述的Lambda表達式,發現compare方法的參數列表與方法體之間多了箭頭標誌「->」,這正是Lambda表達式的特徵標記,箭頭左邊爲匿名方法的參數列表,箭頭右邊爲匿名方法的方法體。注意到參數列表中仍然保留了每一個參數的類型名稱,其實依據compare方法的定義,對於整型數組而言,此處的兩個輸入參數必定是Integer類型,故而參數列表裏的類型名稱能夠通通去掉。這樣進一步簡化後的Lambda表達式變成了下面代碼:程序員
// Lambda表達式第二招。去掉了輸入參數的變量類型 Arrays.sort(intArray, (o1, o2) -> { return Integer.compare(o2, o1); // 按照降序排列 });
儘管上面的Lambda表達式已經足夠簡潔了,但對於這種內部只有一行代碼的方法體來講,還能用點勁繼續壓縮代碼。首先,只有一行代碼的話,包裹方法體的花括號趕忙去掉;其次,compare方法須要一個整型返回值,恰好「Integer.compare(o2, o1)」返回的正是整型數,於是這行代碼前面的return也可去掉,順便把末尾的分號一塊扔了。因而通過三次精簡的Lambda排序代碼以下所示:編程
// Lambda表達式第三招。去掉了方法體的花括號,以及方法返回的return和分號 Arrays.sort(intArray, (o1, o2) -> Integer.compare(o2, o1));
這下終於把Lambda表達式壓縮到了極致,連同sort方法在內都只有短短一行,比起匿名內部類的實現代碼又前進了一大步。
再來一個字符串數組的排序練練手,有利於加深你們對Lambda表達式的理解。在上一篇文章中,對字符串數組按照長度排序的功能,經過匿名內部類的實現代碼是下面這樣的:數組
// 經過匿名內部類對字符串數組按照字符串長度進行排序 private static void sortStrArrayByLength() { String[] strArray = { "說曹操曹操就到", "東道主", "風馬牛不相及", "亡羊補牢", "無巧不成書", "冰凍三尺非一日之寒", "同學", "青出於藍而勝於藍" }; // 字符串數組的默認排序方式爲根據首字母的拼寫順序, // 下面的匿名內部類把排序方式改爲了按照字符串長度進行排序 Arrays.sort(strArray, new Comparator<String>() { @Override public int compare(String o1, String o2) { // 比較先後兩個數組元素的字符串長度大小 return o1.length() < o2.length() ? -1 : 1; } }); String desc = "字符串數組比較字符串長度的升序結果爲:"; for (String item : strArray) { desc = desc + item + ", "; } System.out.println(desc); }
如今把排序器的匿名內部類代碼改寫爲匿名方法,則精兵簡政以後的Lambda表達式縮短到了以下一行代碼:ide
// 下面的Lambda表達式把排序方式改爲了按照字符串長度進行排序 Arrays.sort(strArray, (o1, o2) -> o1.length() < o2.length() ? -1 : 1);
別看Lambda代碼如此精煉,該作什麼編譯器一個都沒落下。運行包含Lambda表達式的測試代碼,輸出的日誌結果明明白白,可見字符串數組果真按照升序排列了。函數
字符串數組比較字符串長度的升序結果爲:同學, 東道主, 亡羊補牢, 無巧不成書, 風馬牛不相及, 說曹操曹操就到, 青出於藍而勝於藍, 冰凍三尺非一日之寒,
更多Java技術文章參見《Java開發筆記(序)章節目錄》測試