Java8 新特性之默認接口方法
圖片.png
摘要: 從java8開始,接口不只是一個只能聲明方法的地方,我們還可以在聲明方法時,給方法一個默認的實現,我們稱之爲默認接口方法,這樣所有實現該接口的子類都可以持有該方法的默認實現。
Java8新特性系列
- Java8新特性(一) – lambda表達式
- Java8新特性(二) – Optional類
- Java8新特性(三) – 流式數據處理
- Java8新特性(四) – 默認接口方法
- 待定
一. 引入默認接口方法的背景
java8可以看做是java版本更新迭代過程中變化最大的一個版本(與時俱進,方能不滅,我們應該感到欣慰),但是經過這麼多年的發展和迭代,java的源碼儼然已是一個龐然大物,要在這樣龐大的體積上大動干戈,肯定不易。所以當第一次看到java8的默認接口方法的時候,我第一感覺就是這是java的設計人員在填自己之前挖的坑。
從前幾篇的講解中我們知道java8在現有的接口上添加了許多方法,比如List的sort(Comparator<? super E> c)
方法。如果按照java8之前接口的設計思路,當給一個接口添加方法聲明的時候,實現該接口的類都必須爲該新添加的方法添加相應的實現。考慮兼容性,這樣是不可取的,所以說這是一個坑,而新的特性又要求不得不爲接口添加一些新的方法,爲了兼得魚和熊掌,java8的設計人員提出了默認接口方法的概念。
這樣說來,默認接口方法似乎是爲api的設計人員而開發的,離我們普通開發人員還有些距離,這樣想有點圖森破啦,雖然我們不用去設計jdk,但是我們在日常的開發過程中還是會有提供api給別的業務方調用的需求,當我們在更新我們api的時候,就可以採用默認方法來提供更加高級的功能,同時保持兼容性。
二. 默認接口方法的定義
默認接口方法的定義很簡單,只要在接口的方法定義前添加一個default
關鍵字即可,如下:
public interface A {
/**
* 默認方法定義
*/
default void method() {
System.out.println("This is a default method!");
}
}
當我們這樣定義一個默認方法之後,所有實現該接口的子類都間接持有了該方法。或者你會和我一樣覺得接口和抽象類越來越像了,確實,不過它們之間還是有如下差別:
1. 一個類只能繼承一個類,但是可以實現多個接口
2. 抽象類可以定義變量,而接口卻不能
抽象除了解決了我們上面提及到的問題,還具有如下好處:
1. 對於一些不是每個子類都需要的方法,我們給它一個默認實現,從而避免我們在子類中對其無意義的實現(一般我們都會throw new UnsupportedException())
2. 默認方法爲java的多重繼承提供了新的途徑(雖然我們只能繼承一個類,但是我們可以實現多個接口啊,現在接口也可以定義默認方法了)
三. 衝突及其解決方法
因爲一個類可以實現多個接口,所以當一個類實現了多個接口,而這些接口中存在兩個或兩個以上方法簽名相同的默認方法時就會產生衝突,java8定義如下三條原則來解決衝突:
1. 類或父類中顯式聲明的方法,其優先級高於所有的默認方法
2. 如果1規則失效,則選擇與當前類距離最近的具有具體實現的默認方法
3. 如果2規則也失效,則需要顯式指定接口
下面通過幾個例子加以說明:
例1
public interface A {
/**
* 默認方法定義
*/
default void method() {
System.out.println("A's default method!");
}
}
public interface B extends A {
/**
* 默認方法定義
*/
default void method() {
System.out.println("B's default method!");
}
}
public class C implements A, B {
public static void main(String[] args) {
new C().method();
}
}
// 輸出:B's default method!
此處因爲接口B相對於A距離C更近,同時B的method是一個具體的默認實現,依據規則2,所以此處實際上調用的是接口B的默認方法
例2
public class D implements A {
}
public class C extends D implements A, B {
public static void main(String[] args) {
new C().method();
}
}
// 輸出:B's default method!
例2在原有接口A、B的基礎上,添加了一個實現接口A的類D,然後類C繼承於D,並實現A和B,此處雖然C離D更近,但因爲D的具體實現在A中,所以B中的默認方法還是距離最近的默認實現,依據規則2,此處實際上調用的是B的默認方法。
例3
// A接口不變
public interface B {
/**
* 默認方法定義
*/
default void method() {
System.out.println("B's default method!");
}
}
public class C implements A, B {
@Override
public void method() {
// 必須顯式指定
B.super.method();
}
public static void main(String[] args) {
new C().method();
}
}
例3中接口B不再繼承自接口A,所以此時C中調用默認方法method()距離接口A和B的具體實現距離相同,編譯器無法確定,所以報錯,此時需要顯式指定:B.super.method()。