概述
函數式接口將分為三個篇章來為大家介紹:
- (應用篇一)(1)函數式接口的來源,(2)Lambda表達式,(3)雙冒號運算符
- (應用篇二)(4)詳細介紹@FunctionInterface注解(5)對java.util.function包進行解讀
- (原理篇)介紹函數式接口的實現原理 應用篇將階段相關的JDK源碼以及給出典型的示例代碼 原理篇則從編譯、JVM維度來分析函數式接口的實現原理,具有一定深度,需要讀者具備一定的底層知識。
說明:源碼使用的版本為JDK-11.0.11
FunctionInterface
這一節,指北君給大家介紹如何聲明一個函數式接口,FunctionInterface注解就是用于來干這件事情的,當我們為接口增加FunctionInterface注解后,編譯器會按照函數式接口的約束進行檢查。先看看注解的定義:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
源碼顯示該注解可用于類、接口和枚舉類型,注解應用于運行時階段。
如果僅從代碼層面我們可能會犯錯誤,比如如下情況:
@FunctionalInterface
public interface IFuncInterfaceSample{
void func1();
void func2();
}
我們會發現編譯器報錯了,這是因為在設計FunctionInterface的時候還增加了額外的約束,這些約束無法在注解的定義中呈現,是通過編譯器實現的,下面指北君就一一為小伙伴們道來。
首先、FunctionInterface是SAM接口,什么是SAM接口呢?全稱Single Abstract Method,從英文字義我們就能明白,接口中只能有一個抽象方法。由于Java在接口中增加了對默認方法和靜態方法的支持,因此采用SAM設計的接口也可以定義默認方法和靜態方法,也就是說我們在使用了FunctionInterface注解的接口中還能夠定義默認方法和靜態方法。除了默認方法和靜態方法外,這里還有一種列外,我們查看java.util.Comparator源碼
@FunctionalInterface
public interface Comparator< T > {
int compare(T o1, T o2);
boolean equals(Object obj);
...
咦,這怎么回事,不是說只能有一個抽象方法么?小伙伴們仔細觀察接口會發現其中一個是equals接口,這不是Object中的方法么?是的,這就是另一條特殊的約束,如果抽象方法是覆蓋的是Object的方法,則不計入抽象方法的個數。
除了抽象方法的個數限制外FunctionInterface只能用于interface,不能用于class和enum,對于這種情形編譯器也會報錯。
小伙伴們有沒想過FunctionInterface為什么要有上面提到的約束呢?相信有些已經隱隱感覺到了:Java是通過類來實現函數式編程的(這部分內容將在原理篇中說明)。
最后,還有一個重點知識:是不是只有使用了@FunctionalInterface才能作為函數式接口呢?相信有不少伙伴和指北君之前一樣理所當然地覺得顯然應該是這樣嘛。但是,JDK的世界很精妙:只要符合函數式接口約束條件的接口,即使沒有采用@FunctionalInterface,編譯器都會處理成函數式接口,比如下面的示例代碼:
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
之前的學習,指北君介紹了什么是函數式編程,如果寫一段Lambda表達式,以及學習如何申明函數式接口。本節我們將學習JDK為我們提供的基礎函數式接口,這些接口位于java.util.function包中,它們可以滿足我們很多通用場景的使用需要。
function包
打開java.util.function包,我們發現里面足足有43個接口,這43個接口就是JDK提供給我們43把劍,如果要讓小伙伴舞動這43把劍,是不是感覺鴨梨山大了呢?小伙伴們,別慌!在你看完指北君后面的分析后,你就可以將這43把劍化為無形,做到手中無劍心中有劍。
指北君先帶領小伙伴們對接口名稱進行分析。是的,是接口名稱,不是源碼
接口名稱解析
從思維導圖中可以看到,接口的主體為最后的單詞,包含:Consumer,Function,Predicate,Supplier,Operator。
接口主體類別
除了主體外,小伙伴是不是還看到了Unary,Binary,Bi這種表示參數個數的修飾詞。
修飾詞 | 作用 |
---|---|
Unary | 一元 |
Binary | 二元 |
Bi | Binary縮寫 |
再就剩下參數類型和方向的修飾詞和參數方向Int,Long,Double,Obj,(X)To(Y)。
通過以上解析,我們可以確定所有的接口都是用于描述函數的輸入參數和返回,這就是java.util.function留在我們心中終極大劍。有了這把終極武器,我們可以對于這43把劍信手拈來,也隨意創造自己的武器,當然,要創造新的武器,按照這43把劍依葫蘆畫瓢是可以,但是要打造精品武器還需要掌握我們接下來介紹的FunctionInterface接口了。
在對整個java.util.function包了然于胸后,我們再打開一個典型的接口看看源碼。function包中的類型都為接口類型,并且使用了@FunctionInterface注解,且每個接口都且只有一個接口(抽象)方法,部分接口存在默認方法和靜態方法。
@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為方便復雜組合場景提供的默認方法。function包的整個代碼邏輯都很簡潔易懂,部分Operator繼承了Function,指北君就不一一贅述了。
小結
至此,函數式接口的應用知識點已經介紹和分析完,涵蓋知識點包含:java.util.function包,FunctionInterface注解,Lambda表達式,雙冒號操作符等知識點。函數式接口在集合,流中應用較為廣泛,也證明了其在數據時候的顯著優勢,各位小伙伴可以結合這些JDK中的源碼進行鞏固加深。
-
接口
+關注
關注
33文章
8694瀏覽量
151927 -
源碼
+關注
關注
8文章
652瀏覽量
29458 -
函數
+關注
關注
3文章
4346瀏覽量
62977 -
代碼
+關注
關注
30文章
4828瀏覽量
69056 -
JDK
+關注
關注
0文章
82瀏覽量
16637
發布評論請先 登錄
相關推薦
評論