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

问题描述 投票:5回答:6

接口是否解决了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

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

我对么?如果我遗失了什么,请赐教。

java interface multiple-inheritance
6个回答
3
投票

Java抵制多个Concrete / abstract类继承,但没有多接口继承,你继承抽象方法没有实现更好的帖子,有良好的解释和例子http://www.tech-knowledgy.com/interfaces-sovles-diamond-death/


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
}

Edit

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问题(实际上是Deadly Diamond of Death问题)无关。然而,关于该主题的大部分文献(以及该答案的早期版本)最终将所有多重继承问题归结为“死亡之钻”。我想这个名字太酷了,只有在技术上合适时才能使用。


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;
}

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


5
投票

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

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

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


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  ? ?
}

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.