2016-05-12 95 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