簡單的說,就是可以在接口中定義一個已實現方法,且該接口的實現類不需要實現該方法;
如下示例:
interface GreetingService { void sayMessage(String message); //可以在接口中定義默認方法 default void sayHello(){ System.out.println("Hello"); } } //實現類不需要實現接口中的默認方法 class GreetingServiceImpl implements GreetingService{ @Override public void sayMessage(String message) { } }
主要是為了方便擴展已有接口;如果沒有默認方法,加入給JDK中的某個接口添加一個新的抽象方法,那麼所有實現了該接口的類都得修改,影響將非常大。
使用默認方法,可以給已有接口添加新方法,而不用修改該接口的實現類。當然,接口中新添加的默認方法,所有實現類也會繼承該方法。
舉個例子,在Java 8的Iterable接口中,新增了一個默認方法forEach,也正因為forEach是默認方法,才不用修改所有Iterable接口的實現類。
Iterable接口新增的forEach方法如下(入參是一個函數式接口,因此支持Lambda表達式):
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
因為Collection接口繼承了Iterable接口,所以我們可以在集合類中使用forEach方法,如下,這裡使用了方法引用(一種更加緊湊的Lambda表達式)
List<String> list = new ArrayList<String>(); list.add("001"); list.add("002"); list.forEach(System.out::println);
可見,我們在未破壞Iterable接口實現類的前提下,給Iterable接口的所有實現類添加了一個新方法forEach,這在Java 8之前是不可能的。
因為一個類是可以實現多個接口的,如果多個接口定義了同樣的默認方法,那麼子類如何調用父類的默認方法呢?
具體調用流程如下:
1、首先,如果子類覆蓋了父類的默認方法,那麼什麼也不用想,直接使用調用子類覆蓋後的方法;
2、其次,優先選擇調用更加具體的接口默認方法,什麼意思呢,舉個例子,如果A1接口繼承A接口,那麼A1接口相對A接口就更加具體,當C類實現了A1接口的時候,就優先調用A1接口的默認方法;
3、最後,如果C類同時實現A1接口和A2接口,且A1和A2有同名的默認方法,那麼選擇哪個接口的默認方法呢?答案是編譯器報錯,提示定義了重名的方法,快速修復方式是覆蓋其中的一個即可;
關於這塊內容,在網上看到一段有意思的代碼,如下,知道為什麼會報錯嗎?如果刪除InterfaceB中的foo方法,是否還會報錯?往InterfaceC中添加foo方法又會怎樣?