一、什麼是預設方法,為什麼要有預設方法
簡單說,就是介面可以有實作方法,而且不需要實作類別去實作其方法。只要在方法名稱前面加個default關鍵字即可。
為什麼要有這個特性?首先,之前的介面是個雙刃劍,好處是面向抽象而不是面向具體編程,缺陷是,當需要修改接口時候,需要修改全部實現該接口的類,目前的java 8之前的集合框架沒有foreach方法,通常能想到的解決辦法是在JDK裡為相關的介面添加新的方法及實作。然而,對於已經發布的版本,是沒法在為介面添加新方法的同時不影響現有的實作。所以引進的預設方法。他們的目的是為了解決介面的修改與現有的實作不相容的問題。
簡單的例子:一個介面A,Clazz類別實作了介面A。
複製代碼代碼如下:
public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
}
public class Clazz implements A {
public static void main(String[] args){
Clazz clazz = new Clazz();
clazz.foo();//呼叫A.foo()
}
}
程式碼是可以編譯的,即使Clazz類別並沒有實作foo()方法。在介面A中提供了foo()方法的預設實作。
二、java 8抽象類別與介面對比
這一個功能特性出來後,很多同學都反應了,java 8的介面都有實作方法了,跟抽象類別還有什麼差別?其實還是有的,請看下表比較。 。
| 相同點 | 不同點 |
1.都是抽象型; 2.都可以有實作方法(以前介面不行); 3.都可以不需要實作類別或繼承者去實作所有方法,(以前不行,現在介面中預設方法不需要實作者實作) | 1.抽象類別不可以多重繼承,介面可以(無論是多重型別繼承或多重行為繼承); 2.抽象類別和介面所反映出的設計理念不同。其實抽象類別表示的是"is-a"關係,介面表示的是"like-a"關係; 3.介面中定義的變數預設是public static final 型,且必須給其初值,所以實作類別中不能重新定義,也不能改變其值;抽象類別中的變數預設為friendly 型,其值可以在子類別中重新定義,也可以重新賦值。 |
三、多重繼承的衝突說明
由於同一個方法可以從不同介面引入,自然而然的會有衝突的現象,預設方法判斷衝突的規則如下:
1.一個宣告在類別裡面的方法優先於任何預設方法(classes always win)
2.否則,則會優先選取最具體的實現,例如下面的範例B重寫了A的hello方法。
輸出結果是:Hello World from B
如果想呼叫A的預設函數,則用到新語法X.super.m(...),下面修改C類,實作A接口,重寫一個hello方法,如下所示:
複製代碼代碼如下:
public class C implements A{
@Override
public void hello(){
A.super.hello();
}
public static void main(String[] args){
new C().hello();
}
}
輸出結果是:Hello World from A
四、總結
預設方法給予我們修改介面而不破壞原來的實作類別的結構提供了便利,目前java 8的集合框架已經大量使用了預設方法來改進了,當我們最終開始使用Java 8的lambdas表達式時,提供給我們一個平滑的過渡體驗。也許將來我們會在API設計中看到更多的預設方法的應用。