概述
函數(shù)式接口將分為三個(gè)篇章來為大家介紹:
- (應(yīng)用篇一)(1)函數(shù)式接口的來源,(2)Lambda表達(dá)式,(3)雙冒號(hào)運(yùn)算符
- (應(yīng)用篇二)(4)詳細(xì)介紹@FunctionInterface注解(5)對(duì)java.util.function包進(jìn)行解讀
- (原理篇)介紹函數(shù)式接口的實(shí)現(xiàn)原理 應(yīng)用篇將階段相關(guān)的JDK源碼以及給出典型的示例代碼 原理篇?jiǎng)t從編譯、JVM維度來分析函數(shù)式接口的實(shí)現(xiàn)原理,具有一定深度,需要讀者具備一定的底層知識(shí)。
說明:源碼使用的版本為JDK-11.0.11
FunctionInterface
這一節(jié),指北君給大家介紹如何聲明一個(gè)函數(shù)式接口,F(xiàn)unctionInterface注解就是用于來干這件事情的,當(dāng)我們?yōu)榻涌谠黾覨unctionInterface注解后,編譯器會(huì)按照函數(shù)式接口的約束進(jìn)行檢查。先看看注解的定義:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
源碼顯示該注解可用于類、接口和枚舉類型,注解應(yīng)用于運(yùn)行時(shí)階段。
如果僅從代碼層面我們可能會(huì)犯錯(cuò)誤,比如如下情況:
@FunctionalInterface
public interface IFuncInterfaceSample{
void func1();
void func2();
}
我們會(huì)發(fā)現(xiàn)編譯器報(bào)錯(cuò)了,這是因?yàn)樵谠O(shè)計(jì)FunctionInterface的時(shí)候還增加了額外的約束,這些約束無法在注解的定義中呈現(xiàn),是通過編譯器實(shí)現(xiàn)的,下面指北君就一一為小伙伴們道來。
首先、FunctionInterface是SAM接口,什么是SAM接口呢?全稱Single Abstract Method,從英文字義我們就能明白,接口中只能有一個(gè)抽象方法。由于Java在接口中增加了對(duì)默認(rèn)方法和靜態(tài)方法的支持,因此采用SAM設(shè)計(jì)的接口也可以定義默認(rèn)方法和靜態(tài)方法,也就是說我們在使用了FunctionInterface注解的接口中還能夠定義默認(rèn)方法和靜態(tài)方法。除了默認(rèn)方法和靜態(tài)方法外,這里還有一種列外,我們查看java.util.Comparator源碼
@FunctionalInterface
public interface Comparator< T > {
int compare(T o1, T o2);
boolean equals(Object obj);
...
咦,這怎么回事,不是說只能有一個(gè)抽象方法么?小伙伴們仔細(xì)觀察接口會(huì)發(fā)現(xiàn)其中一個(gè)是equals接口,這不是Object中的方法么?是的,這就是另一條特殊的約束,如果抽象方法是覆蓋的是Object的方法,則不計(jì)入抽象方法的個(gè)數(shù)。
除了抽象方法的個(gè)數(shù)限制外FunctionInterface只能用于interface,不能用于class和enum,對(duì)于這種情形編譯器也會(huì)報(bào)錯(cuò)。
小伙伴們有沒想過FunctionInterface為什么要有上面提到的約束呢?相信有些已經(jīng)隱隱感覺到了:Java是通過類來實(shí)現(xiàn)函數(shù)式編程的(這部分內(nèi)容將在原理篇中說明)。
最后,還有一個(gè)重點(diǎn)知識(shí):是不是只有使用了@FunctionalInterface才能作為函數(shù)式接口呢?相信有不少伙伴和指北君之前一樣理所當(dāng)然地覺得顯然應(yīng)該是這樣嘛。但是,JDK的世界很精妙:只要符合函數(shù)式接口約束條件的接口,即使沒有采用@FunctionalInterface,編譯器都會(huì)處理成函數(shù)式接口,比如下面的示例代碼:
public interface IFuncInterfaceWithoutAn {
boolean test(int i);
}
public class Demo {
public static void main(String args[]) {
IFuncInterfaceWithoutAn whithout = (x)- >x%2==1;
System.out.println(whithout.test(3));
}
}
java.util.function
之前的學(xué)習(xí),指北君介紹了什么是函數(shù)式編程,如果寫一段Lambda表達(dá)式,以及學(xué)習(xí)如何申明函數(shù)式接口。本節(jié)我們將學(xué)習(xí)JDK為我們提供的基礎(chǔ)函數(shù)式接口,這些接口位于java.util.function包中,它們可以滿足我們很多通用場景的使用需要。
function包
打開java.util.function包,我們發(fā)現(xiàn)里面足足有43個(gè)接口,這43個(gè)接口就是JDK提供給我們43把劍,如果要讓小伙伴舞動(dòng)這43把劍,是不是感覺鴨梨山大了呢?小伙伴們,別慌!在你看完指北君后面的分析后,你就可以將這43把劍化為無形,做到手中無劍心中有劍。
指北君先帶領(lǐng)小伙伴們對(duì)接口名稱進(jìn)行分析。是的,是接口名稱,不是源碼
接口名稱解析
從思維導(dǎo)圖中可以看到,接口的主體為最后的單詞,包含:Consumer,F(xiàn)unction,Predicate,Supplier,Operator。
接口主體類別
除了主體外,小伙伴是不是還看到了Unary,Binary,Bi這種表示參數(shù)個(gè)數(shù)的修飾詞。
修飾詞 | 作用 |
---|---|
Unary | 一元 |
Binary | 二元 |
Bi | Binary縮寫 |
再就剩下參數(shù)類型和方向的修飾詞和參數(shù)方向Int,Long,Double,Obj,(X)To(Y)。
通過以上解析,我們可以確定所有的接口都是用于描述函數(shù)的輸入?yún)?shù)和返回,這就是java.util.function留在我們心中終極大劍。有了這把終極武器,我們可以對(duì)于這43把劍信手拈來,也隨意創(chuàng)造自己的武器,當(dāng)然,要?jiǎng)?chuàng)造新的武器,按照這43把劍依葫蘆畫瓢是可以,但是要打造精品武器還需要掌握我們接下來介紹的FunctionInterface接口了。
在對(duì)整個(gè)java.util.function包了然于胸后,我們再打開一個(gè)典型的接口看看源碼。function包中的類型都為接口類型,并且使用了@FunctionInterface注解,且每個(gè)接口都且只有一個(gè)接口(抽象)方法,部分接口存在默認(rèn)方法和靜態(tài)方法。
@FunctionalInterface
public interface Consumer< T > {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer< T > andThen(Consumer< ? super T > after) {
Objects.requireNonNull(after);
return (T t) - > { accept(t); after.accept(t); };
}
}
accept為核心接口方法,andThen為方便復(fù)雜組合場景提供的默認(rèn)方法。function包的整個(gè)代碼邏輯都很簡潔易懂,部分Operator繼承了Function,指北君就不一一贅述了。
小結(jié)
至此,函數(shù)式接口的應(yīng)用知識(shí)點(diǎn)已經(jīng)介紹和分析完,涵蓋知識(shí)點(diǎn)包含:java.util.function包,F(xiàn)unctionInterface注解,Lambda表達(dá)式,雙冒號(hào)操作符等知識(shí)點(diǎn)。函數(shù)式接口在集合,流中應(yīng)用較為廣泛,也證明了其在數(shù)據(jù)時(shí)候的顯著優(yōu)勢,各位小伙伴可以結(jié)合這些JDK中的源碼進(jìn)行鞏固加深。
-
接口
+關(guān)注
關(guān)注
33文章
8372瀏覽量
150565 -
源碼
+關(guān)注
關(guān)注
8文章
630瀏覽量
29077 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4262瀏覽量
62243 -
代碼
+關(guān)注
關(guān)注
30文章
4700瀏覽量
68108 -
JDK
+關(guān)注
關(guān)注
0文章
80瀏覽量
16566
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
評(píng)論