本文受啓發於Trisha Gee在JavaOne 2016的主題演講Refactoring to Java 8。html
Java 8已經發行兩年多,但不少人仍然在使用JDK7。對企業來講,技術上謹慎未必是壞事,但對我的學習而言,不去學習新技術就極可能被技術拋棄。Java 8一個重要的變動是引入Lambda表達式(lambda expression
),這聽起來彷佛很牛,有種我雖然不知道Lambda表達式是什麼,但我仍然以爲很厲害的感受。不要怕,具體到語言層面上Lambda表達式不過是一種新的語法而已,有了它,Java將開啓函數式編程的大門。java
不要糾結什麼是Lambda表達式、什麼是函數式編程。先來看一下Java 8新的語法特性帶來的便利之處,相信你會過目不忘的。git
在有Lambda表達式以前,要新建一個線程,須要這樣寫:github
new Thread(new Runnable(){ @Override public void run(){ System.out.println("Thread run()"); } }).start();
有Lambda表達式以後,則能夠這樣寫:web
new Thread( () -> System.out.println("Thread run()") ).start();
正如你所見,以前無用的模板代碼不見了!如上所示,Lambda表達式一個常見的用法是取代(某些)匿名內部類,但Lambda表達式的做用不限於此。express
剛接觸Lambda表達式可能以爲它很神奇:不須要聲明類或者方法的名字,就能夠直接定義函數。這看似是編譯器爲匿名內部類簡寫提供的一個小把戲,但事實上並不是如此,Lambda表達式其實是經過invokedynamic指令來實現的。先別管這麼多,下面是Lambda表達式幾種可能的書寫形式,「看起來」並非很難理解。編程
Runnable run = () -> System.out.println("Hello World");// 1 ActionListener listener = event -> System.out.println("button clicked");// 2 Runnable multiLine = () -> {// 3 System.out.println("Hello "); System.out.println("World"); }; BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4 BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5
經過上例能夠發現:api
- Lambda表達式是有類型的,賦值操做的左邊就是類型。Lambda表達式的類型其實是對應接口的類型。
- Lambda表達式能夠包含多行代碼,須要用大括號把代碼塊括起來,就像寫函數體那樣。
- 大多數時候,Lambda表達式的參數表能夠省略類型,就像代碼2和5那樣。這得益於javac的類型推導機制,編譯器能夠根據上下文推導出類型信息。
表面上看起來每一個Lambda表達式都是原來匿名內部類的簡寫形式,該內部類實現了某個函數接口(Functional Interface
),但事實比這稍微複雜一些,這裏再也不展開。所謂函數接口是指內部只有一個接口函數的接口。Java是強類型語言,不管有沒有顯式指明,每一個變量和對象都必須有明確的類型,沒有顯式指定的時候編譯器會嘗試肯定類型。Lambda表達式的類型就是對應函數接口的類型。oracle
Lambda表達式的另外一個重要用法,是和Stream一塊兒使用。Stream is a sequence of elements supporting sequential and parallel aggregate operations。Stream就是一組元素的序列,支持對這些元素進行各類操做,而這些操做是經過Lambda表達式指定的。能夠把Stream看做Java Collection的一種視圖,就像迭代器是容器的一種視圖那樣(但Stream不會修改容器中的內容)。下面例子展現了Stream的常見用法。ide
假設須要從一個字符串列表中選出以數字開頭的字符串並輸出,Java 7以前須要這樣寫:
List<String> list = Arrays.asList("1one", "two", "three", "4four"); for(String str : list){ if(Character.isDigit(str.charAt(0))){ System.out.println(str); } }
而Java 8就能夠這樣寫:
List<String> list = Arrays.asList("1one", "two", "three", "4four"); list.stream()// 1.獲得容器的Steam .filter(str -> Character.isDigit(str.charAt(0)))// 2.選出以數字開頭的字符串 .forEach(str -> System.out.println(str));// 3.輸出字符串
上述代碼首先1. 調用List.stream()
方法獲得容器的Stream,2. 而後調用filter()
方法過濾出以數字開頭的字符串,3. 最後調用forEach()
方法輸出結果。
使用Stream有兩個明顯的好處:
- 減小了模板代碼,只用Lambda表達式指明所需操做,代碼語義更加明確、便於閱讀。
- 將外部迭代改爲了Stream的內部迭代,方便了JVM自己對迭代過程作優化(好比能夠並行迭代)。
假設須要從一個字符串列表中,選出全部不以數字開頭的字符串,將其轉換成大寫形式,並把結果放到新的集合當中。Java 8書寫的代碼以下:
List<String> list = Arrays.asList("1one", "two", "three", "4four"); Set<String> newList = list.stream()// 1.獲得容器的Stream .filter(str -> !Character.isDigit(str.charAt(0)))// 2.選出不以數字開頭的字符串 .map(String::toUpperCase)// 3.轉換成大寫形式 .collect(Collectors.toSet());// 4.生成結果集
上述代碼首先1. 調用List.stream()
方法獲得容器的Stream,2. 而後調用filter()
方法選出不以數字開頭的字符串,3. 以後調用map()
方法將字符串轉換成大寫形式,4. 最後調用collect()
方法將結果轉換成Set
。這個例子還向咱們展現了方法引用(method references
,代碼中標號3處)以及收集器(Collector
,代碼中標號4處)的用法,這裏再也不展開說明。
經過這個例子咱們看到了Stream鏈式操做,即多個操做能夠連成一串。不用擔憂這會致使對容器的屢次迭代,由於不是每一個Stream的操做都會當即執行。Stream的操做分紅兩類,一類是中間操做(intermediate operations
),另外一類是結束操做(terminal operation
),只有結束操做纔會致使真正的代碼執行,中間操做只會作一些標記,表示須要對Stream進行某種操做。這意味着能夠在Stream上經過關聯多種操做,但最終只須要一次迭代。若是你熟悉Spark RDD,對此應該並不陌生。
Java 8引入Lambda表達式,今後打開了函數式編程的大門。若是你以前不瞭解函數式編程,沒必要糾結於這個概念。編程過程當中簡潔明瞭的書寫形式以及強大的Stream API會讓你很快熟悉Lambda表達式的。
本文只對Java Lambda表達式的基本介紹,但願可以激發讀者對Java函數式編程的興趣。若是本文可以讓你以爲Lambda表達式很好玩,函數式編程頗有趣,併產生了進一步學習的慾望,那就再好不過了。文末參考文獻中列出了一些有用的資源。
很是感謝阿里巴巴「西行遊學計劃」給予咱們的支持,感謝阿里中間件團隊資助咱們遠美國赴舊金山參加Java語言的頂級技術會議——2016 JaveOne大會。藉此咱們有機會跟國際頂尖大牛面對面的交流,並第一時間瞭解到Java語言的最新動態。一樣感謝伴我一塊兒遊學的兩位隊友,有ta們在異國他鄉的陪伴和關照,個人西行之旅才更加豐富多彩。此次遊學給咱們帶來的驚喜,將深深留存在咱們的記憶當中。
但願更多的同窗可以關注並參與到阿里巴巴天池大賽當中,也但願阿里可以繼續堅持對青年學生的支持和培養!
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
http://www.slideshare.net/trishagee/refactoring-to-java-8-devoxx-uk
《Java 8函數式編程 [英]沃伯頓》 https://www.oracle.com/javaone/speakers.html#gee