能夠有!!!首先看到這個標題,我想不少跟我同樣的初學者是蒙B的,畢竟百度百科對Java接口是這麼定義的:Java接口是一系列方法的聲明,是一些方法特徵的集合,一個接口只有方法的特徵沒有方法的實現,所以這些方法能夠在不一樣的地方被不一樣的類實現,而這些實現能夠具備不一樣的行爲(功能)。不少書上對接口都是這麼命名的,因此咱們也就引覺得真。直到我用開發工具翻源碼,無心間翻看到java.util.List源碼時,我看到List接口裏面有實現方法,可是個人三觀是崩潰的。html
* @since 1.8 */ default void replaceAll(UnaryOperator<E> operator) { Objects.requireNonNull(operator); final ListIterator<E> li = this.listIterator(); while (li.hasNext()) { li.set(operator.apply(li.next())); } }
後來樓主發現Java8新增了default關鍵字,可使接口有本身的默認的實現類,並且還不影響接口的實現類。java
在Java8發佈之際,有件事情就顯得很是重要,即在不破壞Java現有實現架構的狀況下能往接口裏增長新方法。引入Default方法到Java8,正是爲了這個目的:優化接口的同時,避免跟現有實現架構的兼容問題。看下面例子:api
List<?> list = ... list.forEach(...);// Lambda code goes here |
上面的foreach方法既沒有在java.util.List中聲明,也沒有在java.util.Collection中聲明。(若是要使上面代碼生效)容易想到的方案是在現有的接口中新增foreach方法,並在JDK中必要的地方實現foreach。然而,一經發布,要想在某個接口中增長方法,而不修改現該接口現有的實現類,這是不可能作到的。架構
這樣,即便咱們把Lambda表達式引入到java8中,可是由於不能犧牲向後兼容,而不能夠把Lambda表達式和標準集合類庫結合使用的話,會真的讓人很泄氣。app
爲了解決上述問題,引入了一個新的概念,即虛擬擴展方法(Virtual extension methods),一般也稱之爲 defender 方法,它目前能夠添加到接口中,爲聲明的方法提供默認的實現。函數
簡單地說,Java接口如今能夠有非抽象方法了。Default 方法帶來的好處是,往接口新增一個Default 方法,而不破壞現有的實現架構。工具
儘管如此,Default 方法不適合過多使用,可是對於Java集合API的優化升級,並達到無縫地結合Lambda表達式來講,Default 方法是相當重要的特性。開發工具
讓咱們從最簡單可行的例子開始:下面代碼定義了接口A,以及實現了接口A的Clazz類:優化
public interface A { default void foo(){ System.out.println("Calling A.foo()"); } } public class Clazz implements A {} |
即便Clazz中沒有實現foo(),代碼也能編譯經過。foo()的默認實如今A接口中給出。ui
上例的客戶端代碼以下:
Clazz clazz= new Clazz(); clazz.foo();// Calling A.foo() |
當你們第一次據說default方法,一般會問:「若是一個類實現了兩個接口,而這兩個接口中各自定義了一個同名的default方法,會怎麼樣?」
讓咱們用上一個例子來解釋上述狀況,代碼以下所示:
public interface A { default void foo(){ System.out.println("Calling A.foo()"); } } public interface B { default void foo(){ System.out.println("Calling B.foo()"); } } publicclassClazzimplements A, B {} |
上面代碼編譯失敗,報錯以下:
java: class Clazz inherits unrelated defaults for foo() from types A and B
爲了修復錯誤,在Clazz中,咱們手工地覆寫掉衝突的方法來處理這個問題,以下所示:
public class Clazz implements A, B { public void foo(){} } |
但是,若是咱們就想調用接口A中默認實現的方法foo(),而不用本身實現的,該怎麼辦?那就要像下面這樣使用A#foo()才行。
public class Clazz implements A, B { public void foo(){ A.super.foo(); } } |
Default方法的實例能夠在JDK8中找到。回到以前集合API的forEach 的例子,咱們能夠在接口java.lang.Iterable中找到forEach 的默認實現:
@FunctionalInterface public interface Iterable{ Iterator iterator(); default void forEach(Consumer<?super T> action){ Objects.requireNonNull(action); for(T t: this){ action.accept(t); } } } |
上面的forEach方法使用函數接口java.util.function.Consumer做爲參數,該參數使咱們能傳遞一個Lambda表達式或者方法引用到forEach中,以下所示:
List<?> list = ... list.forEach(System.out::println); |
咱們來看看其實是如何調用default方法的。
從客戶端代碼角度來看,default方法只不過都是初始化的抽象方法。所以default方法也叫-虛擬擴展方法。若是出現上例中的那個類,該類實現了帶default方法的接口,那麼調用default方法的客戶端代碼會在調用端生成invokeinterface 。以下所示
A clazz= new Clazz(); clazz.foo();// invokeinterface foo() Clazz clazz= new Clazz(); clazz.foo();// invokevirtual foo() |
以防出現上述的default方法衝突問題,因此咱們重寫那個default方法,並代理兩個接口之中某一個的default方法的調用,具體代碼請見下面。invokespecial意思是指 咱們將調用專門的實現邏輯。
public class Clazz implements A, B { public void foo(){ A.super.foo();// invokespecial foo() } } |
下面是javap的輸出。
public void foo();
Code:
0: aload_0
1: invokespecial #2 // InterfaceMethodA.foo:()V
4: return
如上面所示的,invokespecial 指令用來調用接口中定義的方法foo()。這從字節碼的角度看也是新東西,由於先前你進行方法調用,僅是經過指向父類的super而不是指向父接口的。
Default方法加入到java中,這是引人關注的事情。Default方法能夠認爲是Lambda表達式和JDK類庫之間的橋樑。引入Default方法的主要目的是爲了升級標準JDK接口,另外也是爲了咱們最終能在Java8中順暢使用Lambda表達式。