hi ! 我是小小,咱們又見面了,今天是本週的第五篇,本篇將會着重的講解一個Java8的新特性。面試
前言從一道面試題提及ide
A:接口裏能夠寫方法嗎?函數
B:能夠的,默認就是抽象方法。繼承
A:那接口裏能夠寫實現方法嗎?接口
B:不能夠,全部的方法必須是抽象的。編譯器
A:你肯定?源碼
B:肯定。。。。it
好吧。這的的確確讓人有點懷疑,因此本文就從這開始着手,io
這裏介紹一個Java8的新特性,接口加強編譯
靜態方法和默認方法咱們能夠在Comparator接口的源碼中,看到大量相似下面這樣的方法聲明。
//default關鍵字修飾的默認方法
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
return thenComparing(comparingInt(keyExtractor));
}
//Comparator接口中的靜態方法
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
其中thenComparingInt()就是一個默認方法,它使用 default 關鍵字修飾。這是Java8引入的新功能,接口中的能夠聲明默認方法和靜態方法。
默認方法帶來的多繼承問題在此以前,Java中的類只支持多重繼承,不支持多繼承,如今有了默認方法,你能夠以另一種方式實現類的多繼承行爲,即,一個類實現多個接口,而這幾個接口都有聲明本身的默認方法。
這裏面引起了一個多繼承問題,設想一下,假如一個類從多個接口中繼承了它們聲明的默認方法,而這幾個默認方法使用的都是相同的函數簽名,那麼程序運行時,類會選擇調用那個方法
@Test
public void test2() {
new C().hello();//result: hello from D
}
interface A {
default void hello() {
System.out.println("heelo from A");
}
}
interface B extends A {
default void hello() {
System.out.println("heelo from B");
}
}
class D implements A{
public void hello() {
System.out.println("hello from D");
}
}
class C extends D implements A, B{
}
這份代碼輸出的結果是 hello from D,能夠看到 C 類的父類D,父接口A,父接口B都定義了一個相同函數簽名的hello(),最後實際調用的是父類D中聲明的方法。
@Test
public void test4() {
new I().hello();//result: heelo from G
}
class I implements G, H { }
interface G extends E {
default void hello() {
System.out.println("heelo from G");
}
}
interface H extends E { }
interface E {
default void hello() {
System.out.println("heelo from E");
}
}
代碼清單二的輸出結果是 hello from G,能夠看到 I 類的父接口 G,父接口E都定義了一個相同函數簽名 hello(),最後實際調用的是父類接口G中聲明的方法。
@Test
public void test3() {
new F().hello(); //result: heelo from E
}
interface A {
default void hello() {
System.out.println("heelo from A");
}
}
interface E {
default void hello() {
System.out.println("heelo from E");
}
}
class F implements A, E {
public void hello() {
//這裏接口A和E再也不具備繼承關係,需顯式的選擇調用接口E或A中的方法,不然沒法經過編譯
E.super.hello();
}
}
第三份代碼,類F必須顯式的覆蓋父接口的hello方法,不然沒法經過編譯器的檢測,由於編譯器沒法肯定父接口A和父接口E中的默認方法哪個優先。這種狀況下,若是你想調用某個父接口的默認方法,可使用 接口名.super.默認方法名這種方式進行調用。
總結Java8的新特性:接口中能夠聲明默認方法和靜態方法。另外,接口默認方法帶來的多繼承問題,即,若是一個類使用相同的函數簽名從多個地方繼承了方法,經過這三種規則能夠判斷
類中的方法優先級最高。類或父類中聲明的方法的優先級高於任何聲明爲默認方法的優先級。
若是沒法依據第一條進行判斷,那麼子接口的優先級更高:函數簽名相同時,優先選擇有最具體實現的默認方法的接口,即若是B繼承了A,那麼B就比A更加具體。
最後, 若是仍是沒法判斷, 繼承了多個接口的類必須經過顯式覆蓋和調用指望的方法, 顯式地選擇使用哪個默認方法的實現(調用語法: 接口名.super.默認方法名 )。