界面是否解决了“致命的死亡钻石”问题?

时间:2012-03-25 14:14:15

标签: java interface multiple-inheritance

接口是否解决了deadly diamond of death问题?

我不这么认为,例如:

// A class implementing two interfaces Interface1 and Interface2.
// Interface1 has int x=10 and Interface2 has int x = 20

public class MultipleInterface implements Interface1, Interface2{

    public void getX(){
        System.out.println(x);
    }
}

这里我们得到一个含糊不清的x

虽然接口是解决方法歧义的好方法,但我猜它们在变量的情况下会失败吗?

我说错了吗?如果我遗失了什么,请启发我。

6 个答案:

答案 0 :(得分:12)

当一个类从父接口继承两个变量时,Java坚持认为对所讨论的变量名的任何使用都是完全限定的。这解决了这个问题。请参阅Java Language Specification Section 8.3

  

类可以继承多个具有相同名称的字段。这种情况本身不会导致编译时错误。但是,在类的主体内通过其简单名称引用任何此类字段的任何尝试都将导致编译时错误,因为这样的引用是不明确的。

类似的声明适用于接口(JLS §9.3)。

the answer by Óscar López中的示例代码非常出色。这是另一个例子:

class Base {
    int x = 10;
}

interface Interface {
    int x = 20;
}

class SingleInheritance implements Interface {
    int y = 2 * x; // ok
}

class MultipleInheritance extends Base implements Interface {
    int y = 2 * x; // compile-time error
    int z = 2 * Interface.x; // ok
}

void aMethod(MultipleInheritance  arg) {
    System.out.println("arg.x = " + arg.x); // compile-time error
    System.out.println("x = " + Interface.x); // ok
}

修改

Java 8为方法引入了一种有限形式的多重继承,因为接口现在可以声明default methods子接口和实现类可以继承。由于类可以实现多个接口,这可能会导致歧义,因为具有相同签名的不同默认方法可以从多个接口继承。 1 Java使用优先级方案处理此问题以指定实际的默认方法遗传。当优先级方案无法产生单个赢家时,它需要显式重写继承的默认方法。

请注意,在任何情况下,Java都不会出现Diamond问题,这是一个非常具体的问题子类,可以带有多重继承。 2 “Diamond”部分指的是类的形状为了解决问题所需的继承图。在C ++中,如果类A继承自两个类B和C,则会出现Diamond问题,每个类都继承自公共基类D.在这种情况下,D的任何公共成员最终在A中出现两次,一次继承B和一次通过C.此外,每当构造或销毁A的实例时,D的构造函数或析构函数最终被调用两次(通常带来灾难性后果,因此名称中的“死亡”部分)。 C ++通过提供虚拟继承解决了这些问题。 (有关详细信息,请参阅讨论here。)

1 注意使用“distinct”一词。如果相同的默认方法是通过两个父接口继承而没有问题,这两个父接口又扩展了定义默认方法的公共基接口;默认方法只是继承。

2 其他多重继承问题 - 比如Java中可能出现的带有接口字段,静态方法和默认方法的歧义 - 技术上与Diamond问题无关(实际上,致命的死亡钻石问题)。然而,关于该主题的大部分文献(以及该答案的早期版本)最终将所有多重继承问题归结为“死亡之钻”。我想这个名字太酷了,只有在技术上合适时才能使用。

答案 1 :(得分:11)

界面不能有属性。当你这样写:

public interface Foo {
    int x;
}

在引擎盖下它会隐式转换为常量,如下所示:

public interface Foo {
    public static final int x;
}

假设您有另一个具有类似命名常量的接口:

public interface Bar {
    int x;
}

如果您在实现xFoo的类中使用Bar值,则必须对这些常量进行限定,不会留下任何歧义的余地,例如:

public class Baz implements Foo, Bar {
    private int y = Foo.x + Bar.x;
}

所以这里没有钻石。无论如何,在界面中声明常量是不受欢迎的,现在大多数时候你最好使用枚举来获得相同的效果。

答案 2 :(得分:5)

不,你没有。除了静态最终变量之外,接口没有任何变量。

如果您实际编写,编译和执行这些接口和类,您将获得答案。 x变量不是类成员,所以没有歧义。

这是您可以通过编写代码并让JDK告诉您自己轻松回答的问题之一。它会比在这里要求更快。

答案 3 :(得分:3)

Java拒绝多个Concrete / abstract类继承但不支持多接口继承,在这种情况下,继承抽象方法并不能实现更好的帖子和良好的解释和示例http://www.tech-knowledgy.com/interfaces-sovles-diamond-death/

答案 4 :(得分:0)

致命的死亡钻石问题。

class A
{
void eat()
{

}
}

B类和C类扩展A和Override eat()方法

class B extends A
{
void eat()
{
}
}


class C extends A
{
void eat()
{
}
}

现在,如果我们有多个继承,将会发生以下情况。

 class D extends B ,C
{
//which eat() method will be inherited here for class D ? a problem  ? ?
}

答案 5 :(得分:0)

致命的死亡钻石是变量的问题,但虚拟方法的问题更严重。如果类Moo1Moo2都继承自类Foo并覆盖抽象虚函数Bar,并且允许类Zoo继承自{ {1}}和Moo1,无需添加自己的Moo2覆盖,我们就不清楚Bar的{​​{1}}方法应该做什么。接口通过要求实现接口的每个类必须为所有接口成员提供自己的实现,并通过指定接口的所有成员在所有接口中被视为相同的接口来直接扩展它或间接接口来避免该问题的。因此,在上述情况下,如果Bar等是接口而不是类,那么实现Zoo的任何类都需要实现Foo,这将与{Zoo同义。 1}},Foo.BarMoo1.Bar