歡迎光臨
每天分享高質量文章

提升Java程式碼質量 —— Lambda

來自:朱小廝的部落格(微訊號:hiddenkafka)

前言

上個月(2018年2月)看過一份調研報告,對於Java版本而言,生產環境中用的最多的是JDk6和JDK7,雖然JDK8在自2014年3月釋出至今使用佔比仍然很小,想想月底JDK10都要出來了呀。JDk8引入了很多新的特性,比如介面預設值、方法取用、Lambda運算式、函式式介面、Optional、Stream等等,這些在其他語言中並不少見的玩意兒,現今在Java中卻還很少使用,很多時候我們會對新事物(其實已經不新鮮)抱著一種比較保守的態度:反正我司還沒用到,學了也無用武之地;讓別人先去踩踩坑、我來大樹底下好乘涼;又學新東西?我JDK7都沒學完,又TMD要學JDk8、9、10?累覺不愛啊,筆者也一直保持著這種心態,但是一顆好奇的心促使我小小的往前邁了那麼一小步……

好奇之心

平常擼程式碼的時候經常也會涉及一些多執行緒的東西,比如使用Future和Callable來搞一些事情,程式碼舉例如下:

對於這段程式碼在搞什麼事情這裡就不多說了,值得註意的是IDEA裡給“new Callable()”這段程式碼“特殊”照顧了一下(註意IDE的language level要設定成JDK8及以上的),想必是要告訴我一些隱晦的事情,本著一顆少男的好奇心我默默的瀏覽了下提示資訊,詳細如下圖:

內心的躁動讓我小小點選了“Replace with lambda”一下?程式碼迅速地做了切換:

What an AMAZING thing! 程式碼立馬簡潔了許多,整個Callable介面的實現只保留了關鍵的“()->”lambda demo””。這個不止在使用Callable的時候發生,常用的Runnable、Comparator中都會如此,JDK8到底對它們做了什麼改變?! 我們不妨舉一個DEMO從最初的夙願來一步步撥開這一層層面紗~~

循序漸進

DEMO需求:對一個數字型的字串串列做排序,這個串列很簡單,具體如下:

List list = Arrays.asList("2", "3", "1", "4");

這個需求很簡單,實現起來不需要1分鐘:

Comparator comparator = new Comparator() {
   @Override
   public int compare(String o1, String o2) {
       return Integer.valueOf(o1) - Integer.valueOf(o2);
   }
};
list.sort(comparator);

註意本文中所使用的JDK版本為8,所以你看到List的sort()方法的時候不要感到太意外,這是List介面中用預設方法實現的一個新方法:default void sort(Comparator c)。對於上面的實現我們還可以很傲嬌的改用一下匿名類的方式:

list.sort(new Comparator() {
   @Override
   public int compare(String o1, String o2) {
       return Integer.valueOf(o1) - Integer.valueOf(o2);
   }
});

如果是JDK7,那麼用一下Collections.sort()方法,然後差不多實現到這裡就結束了,然後JDK8才剛剛開始,我們可以透過進一步的把這個方法改為Lambda運算式的形式實現:

list.sort((String o1, String o2) -> Integer.valueOf(o1) - Integer.valueOf(o2));

好了,一下子就變成一行程式碼了,只不過看上去比原先的有那麼一丟丟的晦澀。正如上面所說的Callable也好、Runnable也好,和Comparator這種都屬於函式式介面,如果你開啟原始碼可以發現這三個介面都有一個相同的註解——@FuncationalInterface。如果你所使用的方法中包含有函式式介面,那麼你就可以使用Lambda運算式。函式式介面是一種有且只有一個抽象方法的介面,如果標註為@FunctionalInterface的介面沒有抽象方法(空介面也就是標記介面,或者介面中的方法都是預設方法)或者擁有超過一個抽象方法的話都會報錯。
類似於( //會報錯:No target method found.):

@FunctionalInterface  
public interface FunctionError{
}

或者這樣(//會報錯:Multiple non-overriding abstract methods  found in interface FunctionError):

@FunctionalInterface
public interface FunctionError{
   public String method1(String o1, String o2);
   public String method2(Integer o1, Boolean o2);
}

都是無效的,可以將上面的其中一個方法改為預設方法:

@FunctionalInterface
public interface FunctionCorrect{
   public String method1(String o1, String o2);
   default public String method2(Integer o1, Boolean o2){
       return "why not rabbitmq or kafka?";
   };
}

這樣就沒有問題。

這裡我們先來小結一下——Lambda運算式一個有三個部分:

  1. 引數串列。對於上面的Comparable介面而言其引數串列就是“public int compare(String o1, String o2)”中的“(String o1, String o2)”。對於Callable/Runnable來說,其call/run方法沒有引數,所以這裡可以表示為一個簡單的括號()。

  2. 箭頭符號:->。

  3. Lambda運算式主體。也就是“Integer.valueOf(o1) – Integer.valueOf(o2);”。對於本文中第一個示例而言,其Lambda主題即為:“”lambda demo.””。Lambda運算式主體的傳回值型別 = 函式式介面中方法的傳回值型別相同。
    可以看到Lambda的基本語法為:

(引數串列)-> 運算式 

或者:

(引數串列)-> {陳述句}

Java還可以推斷出Lambda運算式中的引數型別,上面的程式碼還可以進一步最佳化去掉引數型別的宣告:

list.sort((o1, o2) -> Integer.valueOf(o1) - Integer.valueOf(o2));

對於上面的程式碼中Integer本身就具有“可比性”(實現了Comparable)介面,上面的程式碼可以改寫成:

list.sort((o1, o2) -> Integer.valueOf(o1).compareTo(Integer.valueOf(o2)));

不過這樣的程式碼易讀性好像也不是很高,我們這裡做進一步的改進,這裡引入了一個新的概念——方法取用。方法取用讓你可以重覆使用現有的方法定義,並像Lambda一樣傳遞他們,在某些情況下,他們更加的易讀。上面的程式碼可以進一步的改寫成:

list.sort(Comparator.comparing(Integer::valueOf));

這樣的程式碼可以看出我們對於String串列的排序規則時其int型別的值,而不用再關註有點晦澀的Lambda陳述句。如果有一天寶寶不開心了,不按照其轉換的int值排序,而是按照其hashCode排序怎麼辦呢,很簡單:

list.sort(Comparator.comparing(String::hashCode));

如果寶寶又不開了,原本是升序排序的,現在要按降序排序的怎麼辦呢?改寫Lambda運算式,比如這樣:

list.sort((o1, o2) -> Integer.valueOf(o2).compareTo(Integer.valueOf(o1)));

不如這樣:

list.sort(Comparator.comparing(Integer::valueOf).reversed());
//or
list.sort(Comparator.comparing(String::hashCode).reversed());

是不是通俗易懂又方便?方法取用的基本思想是:如果一個Lambda代表的只是直接呼叫這個方法,那最好還是用名稱來呼叫它,而不是去描述如何呼叫它。事實上,方法取用就是讓你根據已有的方法實現來建立Lambda運算式,只不過顯示地指明方法的名稱,程式碼可讀性會高一點。當你需要使用方法取用時,將標的取用放在分隔符::前,而方法的名稱放在後面。例如:

(String s) -> System.out.println(s) 可以等效為 System.out:println

要不你try一下下麵的程式碼看看效果是不是一樣的?

public class FunctionDemo {
   @FunctionalInterface
   public interface FunctionQuote{
       public void print(String arg);
   }
   public static void process(FunctionQuote functionQuote){
       String str = "http://blog.csdn.net/u013256816";
       functionQuote.print(str);
   }
   public static void main(String[] args) {
       process((String s) -> System.out.println(s));
       process(System.out::println);
   }
}

總結

不好意思,lambda有點diao, 總結必須傲嬌的太監了。。^-^


●編號695,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

 

Python程式設計

更多推薦:18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂