2016-05-12 94 views
6

比方說,有以下幾種類型:的Java 8默認方法繼承

public interface Base { 

    default void sayHi(){ 
     System.out.println("hi from base"); 
    } 
} 

public interface Foo extends Base { 
    @Override 
    default void sayHi(){ 
     System.out.println("hi from foo"); 
    } 
} 

public interface Bar extends Base { 
} 

public class MyClass implements Foo, Bar { 
    public static void main(String[] args) { 
     MyClass c = new MyClass(); 
     c.sayHi(); 
    } 
} 

在這種情況下,如果main被執行,「從富喜」被打印出來。爲什麼Foo的實施優先? BarBase繼承sayHi(),因爲如果MyClass僅用於執行Bar,那麼將調用Base實現?所以代碼仍然不能編譯是有意義的。此外,由於Bar應有的Base實施sayHi(),爲什麼我不能覆蓋它在MyClass,如:

@Override 
public void sayHi() { 
    Bar.super.sayHi(); 
} 

試圖這樣做時,會出現以下錯誤:

壞類型修飾符在缺省超級調用方法中,sayHi()在Foo中被覆蓋

+0

@Casey什麼IDE您使用的? Eclipse給出了另一個錯誤。 –

+0

@JornVernee這是來自IntelliJ。 Eclipse輸出了什麼? – Casey

+0

@Casey'''非法引用來自類型Base的超級方法sayHi(),無法繞過來自類型Foo''的更具體的覆蓋。 –

回答

9

這個行爲幾乎用你的確切示例在JLS 9.4.1中指定,只是wi TH一些名字各地更改:

interface Top { 
    default String name() { return "unnamed"; } 
} 
interface Left extends Top { 
    default String name() { return getClass().getName(); } 
} 
interface Right extends Top {} 

interface Bottom extends Left, Right {} 

右(左起) 不是正繼承名()的頂部,但底部繼承名()。這是因爲來自Left的name()覆蓋了Top()中name()的聲明 。

JLS似乎沒有給出任何特別具體的理由,我可以看到;這正是Java設計者決定如何繼承的原因。

+0

有道理。有點像設計缺陷。永久覆蓋默認方法將隱藏最初的實現,這在複雜的層次結構中可能至關重要。似乎讓具體的類決定使用哪個實現將是一個更好的主意。 – aiguy

+3

@aiguy這是...重要的一點。這對於普通的類繼承來說是正確的,覆蓋隱藏了超類型實現。 –

+0

好吧,在正常的類繼承中,你永遠不可能有2個父實現。我的觀點是,如果說在複雜的層次結構中有人添加了原始默認實現的重寫,如果某些具體類依賴於原始實現,則可能會破壞整個層次結構。 – aiguy

9

這是設計。從JLS 15.12.3

如果表單是TypeName。超級。 [TypeArguments]標識符,然後:

  • 如果類型名表示接口,設T爲直接封閉方法調用中的類型聲明。如果存在一個方法中,從編譯時聲明不同,重寫(§9.4.1)從一個直接超類或T.
  • 的直接超接口

在這種情況下的編譯時聲明發生編譯時間錯誤超級接口覆蓋在祖父接口中聲明的方法,該規則通過簡單地將祖父母添加到其直接超級接口列表中來防止子接口「跳過」覆蓋。訪問祖父母功能的適當方式是通過直接超級接口,並且只有當該接口選擇暴露所需的行爲。 (另外,開發人員可以自由定義暴露了超級方法調用所需的行爲他自己額外的高級接口。)

+0

在這和[其他答案](http://stackoverflow.com/a/37197609/5221149)之間,問題已被全部解答。現在,哪個答案被接受?嗯.... – Andreas