轉自:https://www.xttblog.com/?p=3453
函數式接口(Functional Interface)
函數式接口(Functional Interface)是 Java 8對一類特殊類型的接口的稱呼。 這類接口只定義了唯一的抽象方法的接口(除了隱含的Object對象的公共方法), 因此最開始也就做SAM類型的接口(Single Abstract Method)。
說白了,所謂的函數式接口,當然首先是一個接口,然后就是在這個接口里面只能有一個非 Object 對象的公共方法的抽象方法,可以有多個靜態方法和默認方法。
上面說的是概念,如果你還沒看懂,沒關系,我們繼續通過下面的幾個例子,我相信你就會明白。
JDK 8之前已有的函數式接口
JDK 8之前已有的 JDK 中提供的支持函數式編程的函數式接口。
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
Java8 中新定義的函數式接口
Java8 中有一個 java.util.function 包。其中定義了幾組類型的函數式接口以及針對基本數據類型的子接口。
- Predicate:傳入一個參數,返回一個bool結果, 方法為boolean test(T t)
- Consumer:傳入一個參數,無返回值,純消費。 方法為void accept(T t)
- Function:傳入一個參數,返回一個結果,方法為R apply(T t)
- Supplier:無參數傳入,返回一個結果,方法為T get()
- UnaryOperator:一元操作符, 繼承Function,傳入參數的類型和返回類型相同。
- BinaryOperator:二元操作符, 傳入的兩個參數的類型和返回類型相同, 繼承 BiFunction
下面我們來看一個案例。
1 @FunctionalInterface 2 public interface XttblogService { 3 void sayMessage(String message); 4 }
那么我們現在就可以使用 Lambda 表達式來表示該接口的一個實現(注:JAVA 8 之前一般是用匿名類實現的):
XttblogService xttblogService = message -> System.out.println("Hello " + message);
@FunctionalInterface 注解的接口,只能有一個 public 接口。
如果定義了兩個,就會報錯。
但是我們可以定義多個默認方法。因為默認方法不是抽象方法,其有一個默認實現,所以是符合函數式接口的定義的。
函數式接口里允許定義默認方法和靜態方法,上面的兩種寫法都不會報錯。
另外,函數式接口里還允許定義 java.lang.Object 里的 public 方法。
函數式接口里是可以包含 Object 里的 public 方法,這些方法對於函數式接口來說,不被當成是抽象方法(雖然它們是抽象方法);因為任何一個函數式接口的實現,默認都繼承了 Object 類,包含了來自 java.lang.Object 里對這些抽象方法的實現。
我們常用的一些接口 Callable、Runnable、Comparator 等在 JDK8 中都添加了 @FunctionalInterface 注解。
那么 Java 中為什么需要 @FunctionalInterface 注解呢?
沒有這個注解,我們也可以實現 Lambda 表達式。
但是 Java 推出 @FunctionalInterface 注解的原因是在 Java Lambda 的實現中,開發組不想再為 Lambda 表達式單獨定義一種特殊的 Structural 函數類型,稱之為箭頭類型(arrow type),依然想采用 Java 既有的類型系統(class, interface, method等)。增加一個結構化的函數類型會增加函數類型的復雜性,破壞既有的 Java 類型,並對成千上萬的 Java 類庫造成嚴重的影響。權衡利弊,因此最終還是利用 SAM 接口作為 Lambda 表達式的目標類型。
JDK 中已有的一些接口本身就是函數式接口,如 Runnable。JDK 8 中又增加了 java.util.function 包,提供了常用的函數式接口。
函數式接口代表的一種契約,一種對某個特定函數類型的契約。在它出現的地方,實際期望一個符合契約要求的函數。Lambda 表達式不能脫離上下文而存在,它必須要有一個明確的目標類型,而這個目標類型就是某個函數式接口。