java8學習之深入函數式接口與方法引用


函數式接口:

函數式接口【FunctionalInterface】是整個Lambda表達式的一個根源,換句話來說java8中的Lambda表達式要想徹底掌握,前提是要徹底理解好函數式接口,所以這次繼續對函數式接口進行鞏固。

先回顧一下上一次通過讀FunctionalInterface這個注解的javadoc之后的三點總結【參考:http://www.cnblogs.com/webor2006/p/8111585.html】:

關於FunctionalInterface的doc上有一個細節還需要注意,在上次中也已經提到過,這里再擰出來看一下:

那換成代碼如何來理解上面這段話呢?新建一個接口,里面聲明一個方法,當然它是抽象的【抽象的概念是只有聲明木有具體實現的】:

那這個是不是FuncationalInterface呢?加上注解就可以論證了:

那如果再增加一個抽象方法:

看下報錯提示:

那如果此時將這個新加的方法名稱換一個是Object類中的呢?

那為什么呢?原因就如javadoc上面的這點所描述:toString()是一個抽象方法,但是Object中也有此方法,細心的可以發現其實開發工具比較智能的在該方法的左側已經顯示出來一個箭頭,點擊則可以查看它父類的方法:

那點開看一下唄:

很顯然該方法是復寫的Object類的中方法,所以java編譯器不認為該方法是一個抽象方法,所以當然整個接口還是滿足只有一個抽象方法的條件,當然認為此時的接口還是一個函數式接口啦。這是表現上的理論,那為啥要有這樣的一個規定呢?其實也比較好理解:如果一個類實現該接口,那很明顯該類一定有這兩個方法的實現,然而java.lang.Object是所有類的父類,也就是說明具體類都會直接或間接的繼承Object類中的方法,而toString()並非是子類特有的方法,所以說如果一個方法中聲明的剛好是Object類中的方法,那它不算抽象方法。

接下來繼續用代碼來進行延深:

由於MyInterface是函數式接口,所以可以改用Lambda表達式,如下:

其實上面標紅的Lamdba表達式的寫法就是MyInterface的匿名實現類,所以程序可以這樣寫:

那咱們可以打印一下這個類和它父類名字,如下:

那這個myInterface類的具體實現的接口是哪些呢?接着可以打印一下:

那這接口是誰呢?繼續打印:

通過上面的例子對於函數式接口應該有一個比較好的認識了,所以對於它的探討先暫時到這,接下來對於之前的例子進行一個進一步的探討,回顧下當時的代碼:

通過三種方式來對一個集合進行遍歷,這里將重點觀注在最后一種用函數式接口的方式,那這個forEach方法是來自於List類中么?點擊查看下源碼:

來自於Iterable接口當中,可以看到該方法是從Java1.8才開始引入的,但是Iterable是從1.5就開始引入的:

而我們知道List最終是實現了Iterable這個接口,所以當然也就繼承有forEach這個方法啦,這就解釋了為啥可以通過List去直接調用forEach來達到遍歷的目的。

這里需要注意一下細節,這個forEach方法的具體實現實際上就是寫在Iterable接口當中的,但是在接口的聲明前面有個default關鍵字,這個也在之前說了,在Java8以后在接口中可以有具體實現了,但凡在接口中有具體實現方法,前面必須加default的關鍵字,這稱之為默認方法(Default Method),而對於實現這個接口的類也自然而然繼承有這個默認方法了,有點像抽象類的概念:類中既可有抽象方法,也可以有具體方法,而繼承類也會繼承抽象類的具體方法。

接着來查看一下forEach javadoc的注釋:

接下來再來看一下這個指定的動作Consumer,從字面意思來理解當然就是消費者的意思啦,點擊看一下它的源碼:

讀一下接口的doc:

再回到咱們的程序來說,很顯然可以換成Lambda表達式來改造,所有函數式接口都可以采用Lambda表達式來編寫,如下:

下面再對Lambda表達式進行一個總結。

Lambda表達式作用:

  • Lambda表達式為Java添加了缺失的函數式編程特性,使得我們能將函數當做一等公民對待。
    因為Java在以前方法永遠都是依附於類而存在的,不可以獨立存在的, 現在我們可以將方法當作參數進行傳遞了, 所以函數在Java8里面就成了一等公民。
  • 在將函數作為一等公民的語言中,Lambda表達式的類型是函數,但在Java中,Lambda表達式是對象,他們必須依咐於一類特別的對象類型---函數式接口(Functional Interface)
    標紅的說Lambda表達式是對象,為什么呢?

迭代方式:

外部迭代:
什么是外部迭代呢?看程序:

下面用圖來更形象的理解:

然后一個個元素進行迭代,最后指向一個空的元素既迭代完成了,如下:

內部迭代:

何為內部迭代,直接看代碼:

之所以叫內部迭代,相對於外部迭代,當然是沒有了外部迭代的迭代器啦,不借助於外部力量既完成元素的迭代。

方法引用:

對於上面元素迭待的方式已經改用Lambda表達式去寫,代碼已經很精簡了,但是!!還可以更加精簡,如下:

對於上面這種寫法就叫做方法引用(method references),而這個forEach方法參數是函數式接口的實例,那意思是這個方法引用能創建函數式接口的實例?是的,在查看函數式注解的javadoc上就已經清楚的說明了,這里再來回顧一下:

這里看一個IDE比較智能的地方,就是在方法引用語句中的"::"處點擊ctrl鍵之后會看到:

自動就跳到了Consumer這個函數式接口了,說明編譯器識別到了這種定法就是對Consumer接口的實現,關於方法引用在之后還會仔細學習,這里有個感性的認識就行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM