函數式接口
1. 函數式接口定義
- 一個接口有且只有一個抽象方法。
- 函數式接口的實例可以通過 lambda 表達式、方法引用或者構造方法引用來創建。
注意:
- 如果一個接口只有一個抽象方法,那么該接口就是一個函數式接口
- 如果我們在某個接口上聲明了
@FunctionalInterface
注解,那么編譯器就會按照函數式接口的定義來要求該接口
函數式接口應滿足:
- 該接口是一個接口類型而不是注解類型,枚舉類型或類類型
- 被聲明
@FunctionalInterface
注解的接口應該滿足函數式接口的定義如果不滿足上述條件,編譯器就會生成錯誤信息。
- 如果某個接口只有一個抽象方法,但我們並沒有給該接口聲明
@FunctionalInterface
注解,那么編譯器依舊會將該接口看作是函數式接口
重寫 Object 類里的方法不會導致函數式接口失效
如果一個接口聲明了抽象方法,但該抽象方法重寫了 Object 類里的一個公有方法,那么對於 Java 編譯器來說,它並不會認為該方法符合函數式接口的抽象方法(即不把該方法當作函數式接口的抽象方法)。因為接口的實現類都會直接或間接繼承 Object 這個根類,所以在函數式接口中定義與 Object 類中簽名一樣的方法是不會導致函數式接口失效的。
舉例:
舉例的接口聲明為函數式接口,並重寫了 toString()
這個 Object()
方法,但是並不會報錯:
@FunctionalInterface
public interface MyInterface {
void test();
String toString();
}
2. 默認方法 default-method
jdk1.8 以后接口里面可以定義方法的實現,這種方法叫做 default-method。
舉例:
舉個例子:forEach方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
該實現方法定義在 Iterable 中。
3. 靜態方法 static-method
jdk1.8 后接口也可以定義靜態方法。
舉例:
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
4. 使用 default method 注意
4.1 類實現接口方法
如果接口聲明了 default 方法,並且某類實現了該接口,那么 default 方法將會被繼承。
這里有個問題:
如果有一個類繼承了兩個不同接口的同名 default 方法,jvm 編譯器是無法識別到底該使用哪個方法的,必須重寫 default 方法,如下:
public class MyClass implements MyInterface1, MyInterface2 {
@Override
public void myMythod() {
MyInterface2.super.myMethod();
}
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.myMethod();
}
}
可以重寫也可以在重寫方法中指定要繼承接口的 default 方法。
4.2 子類繼承父類並實現接口
實現類的優先級比接口高。
public class MyClass extends MyInterfaceImpl implements MyInterface2 {
public stativ void main(String[] args) {
MyClass myClass = new MyClass();
myClass.myMethod();
}
}
MyInterfaceImpl 中有個和 MyInterface2 同名的 myMethod()
方法。
運行上述代碼,編譯器並不會報錯,因為實現類的優先級比接口高,所以 myClass.myMethod()
調用的方法默認是 MyInterfaceImpl 類中的實現方法。