理解Functional Interface(函數式接口,如下簡稱FI)是學習Java8 Lambda表達式的關鍵所在,因此放在最開始討論。FI的定義其實很簡單:任何接口,若是隻包含惟一一個抽象方法,那麼它就是一個FI。爲了讓編譯器幫助咱們確保一個接口知足FI的要求(也就是說有且僅有一個抽象方法),Java8提供了@FunctionalInterface註解。舉個簡單的例子,Runnable接口就是一個FI,下面是它的源代碼:html
爲了可以方便、快捷、幽雅的建立出FI的實例,Java8提供了Lambda表達式這顆語法糖。下面我用一個例子來介紹Lambda語法。假設咱們想對一個List<String>按字符串長度進行排序,那麼在Java8以前,能夠藉助匿名內部類來實現:java
上面的匿名內部類簡直能夠用醜陋來形容,惟一的一行邏輯被五行垃圾代碼淹沒。根據前面的定義(並查看Java源代碼)可知,Comparator是個FI,因此,能夠用Lambda表達式來實現:程序員
代碼變短了好多!仔細觀察就會發現,Lambda表達式,很像一個匿名的方法,只是圓括號內的參數列表和花括號內的代碼被->分隔開了。垃圾代碼寫的越少,咱們就有越多的時間去寫真正的邏輯代碼,不是嗎?是的!圓括號裏的參數類型是能夠省略的:express
若是Lambda表達式的代碼塊只是return後面跟一個表達式,那麼還能夠進一步簡化:編程
注意,表達式後面是沒有分號的!若是隻有一個參數,那麼包圍參數的圓括號能夠省略:oracle
若是表達式不須要參數呢?好吧,那也必須有圓括號,例如:app
有時候Lambda表達式的代碼就只是一個簡單的方法調用而已,遇到這種狀況,Lambda表達式還能夠進一步簡化爲方法引用(Method References)。一共有四種形式的方法引用,第一種引用靜態方法,例如:ide
第二種引用某個特定對象的實例方法,例如前面那個遍歷並打印每個word的例子能夠寫成這樣:函數式編程
第三種引用某個類的實例方法,例如:函數
第四種引用類的構造函數,例如:
既然Lambda表達式這麼好用,那麼,能夠在哪些地方使用呢?若是你真正明白了什麼是FI(很容易),應該馬上就能給出答案:任何能夠接受一個FI實例的地方,均可以用Lambda表達式。好比,雖然上面給出的例子都是把Lambda表達式看成方法參數傳遞,但實際上你也能夠定義變量:
Java8除了給Runnable,Comparator等接口打上了@FunctionalInterface註解以外,還預約義了一大批新的FI。這些接口都在java.util.function包裏,下面簡單介紹其中的幾個。
Predicate用來判斷一個對象是否知足某種條件,好比,單詞是否由六個以上字母組成:
Function表示接收一個參數,併產生一個結果的函數:
下面的例子將集合裏的每個整數都乘以2:
Consumer表示對單個參數進行的操做,前面例子中的forEach()方法接收的參數就是這種操做:
爲了充分發揮Lambda的威力,Java8對不少老的類庫進行了加強,給它們配備了Lambda武器。好比前面例子中用到的forEach()方法,其實是添加到Iterable接口中的。而屢次出現的stream()方法,則是添加在了Collection接口裏。用過Ruby,Scala,Groovy等語言的Java程序員,可能已經對在這些語言裏很好實現的外部迭代器模式垂涎好久了。雖然Google的Guava能夠在必定程度上彌補Java的這種缺陷,可是Java8的Lambda才真正讓Java朝着函數式編程邁進了一大步。
細心的讀者可能會發現一個問題,給Iterable和Collection等接口增長方法,豈不是會破壞接口的向後兼容性?是的,爲了保證API的向後兼容性,Java8對接口的語法進行了較大的調整,增長了默認方法(Default Methods)。下面是forEach()方法的實現代碼:
除了抽象方法和默認方法,從Java8開始,接口也能夠有靜態(static)方法了。有了這個語法,咱們就能夠把和接口相關的幫助方法(Helper Methods)直接定義在接口裏了。好比Function接口就定義了一個工廠方法indentity():
和內部類同樣,Lambda也能夠訪問外部(詞法做用域)變量,規則基本同樣。Java8以前,內部類只能訪問final類型的變量,Java8放寬了這種限制,只要變量實際上不可變(effectively final)就能夠。換句話說,若是你給變量加上final關鍵字編譯器也不報錯,那麼去掉final關鍵字後,它就是effectively final的。看下面的例子:
在Java8以前,a必須是final的才能被x看到。下面用Lambda表達式重寫上面的例子:
能夠看到,爲了支持Lambda表達式,Java8對Java語言作了很大的調整。但Lambda表達式並不是只是Java語法糖,而是由編譯器和JVM共同配合來實現的,這一點我會在下一篇文章裏詳細介紹