來自:朱小廝的部落格(微訊號: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
內心的躁動讓我小小點選了“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運算式一個有三個部分:
-
引數串列。對於上面的Comparable介面而言其引數串列就是“public int compare(String o1, String o2)”中的“(String o1, String o2)”。對於Callable/Runnable來說,其call/run方法沒有引數,所以這裡可以表示為一個簡單的括號()。
-
箭頭符號:->。
-
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、資料庫、運維等。