【问题标题】:Multiple Inheritance Ambiguity with Interface具有接口的多重继承歧义
【发布时间】:2015-06-27 18:54:54
【问题描述】:

我们都知道diamond problem关于多重继承 -

   A
  / \
 B   C
  \ / 
   D

这个问题描述了类D 的模棱两可的情况。如果类A 有一个方法,并且B 和/或C 中的任何一个都覆盖了该方法,那么D 覆盖的是哪个版本的方法?

这个问题是否也适用于 Java 中的接口?如果不是,Java 接口如何克服这个问题?

【问题讨论】:

标签: java oop inheritance interface diamond-problem


【解决方案1】:

具有虚拟声明的接口,它们不会有实现,因此没有 歧义问题。

【讨论】:

    【解决方案2】:

    添加关于 Java8 多继承与接口的现有答案(也就是 Java 如何仍然避免菱形问题):

    要遵循三个规则:

    1. 班级总是获胜。类自己的方法实现优先于接口中的默认方法。

    2. 如果类没有:最具体的接口获胜

    1. 如果不是上述情况,继承类必须明确说明它正在使用哪个方法实现(否则无法编译)

    【讨论】:

    • 其实第一点和第三点描述的是类实现优先
    【解决方案3】:

    Java 不支持多重继承,所以不会出现菱形问题。如果 B & C 是接口,那么接口中就没有实现。即使 B & C 覆盖接口 A 中的方法(不能是类),这些方法也将具有相同的签名。关于使用哪个实现没有歧义,因为没有实现。

    【讨论】:

    • 正如 Daniel Pryden 所说,Java 8 的事情变得有点棘手。
    • 从 Java 8 到 Java 7 之前,接口中的任何实现都有效,即使接口可以有具体的方法。
    【解决方案4】:

    在Java 8中引入的接口中的默认方法,可能会出现多继承相关的问题,有3种情况 -

    1- 如果实现类覆盖默认方法并为默认方法提供自己的功能,则该类的方法优先于接口默认方法。

    2-当类实现了两个接口并且都具有相同的默认方法时,并且该类没有覆盖该方法,则会引发错误。

    3-如果一个接口扩展了另一个接口,并且都具有相同的默认方法,则继承接口的默认方法将优先。

    阅读更多关于它的信息here

    【讨论】:

      【解决方案5】:

      即使接口可以有方法的默认实现,Java 也克服了这个问题,因为默认实现要么是明确的(类A 中的那个),要么这种情况被某些人解决了规则(当B 类或C 类覆盖A 类的实现时,见下文)。

      当类或接口的超类型提供多个具有相同签名的默认方法时:

      • 实例方法优于接口默认方法。
      • 已被其他候选者覆盖的方法将被忽略。 当超类型共享一个共同祖先时,可能会出现这种情况

      但是,如果两个或多个独立定义的默认方法冲突,或者默认方法与抽象方法冲突,则 Java 编译器会产生编译器错误。您必须显式覆盖超类型方法。在这种情况下,您可以使用 super 关键字调用任何默认实现。

      另请参阅:How does Java 8' new default interface model works (incl. diamond, multiple inheritance, and precedence)?

      【讨论】:

        【解决方案6】:

        菱形问题仅适用于实现继承(extends 在 Java 8 之前的所有 Java 版本中)。它不适用于 API 继承(implements 在 Java 8 之前的所有 Java 版本中)。

        由于具有匹配类型签名的接口方法是兼容的,如果您继承相同的方法签名两次不会出现菱形问题:匹配的方法签名只是合并。 (如果类型签名不同,那么您也不存在菱形问题。)

        在 Java 7 及更低版本中,继承实现代码的唯一方法是通过 extends 关键字,它最多限制为一个父级。因此不存在多重实现继承,也不存在菱形问题。

        Java 8 增加了一个新问题,因为它允许接口具有实现代码。当您使用具有匹配签名的方法实现多个接口时,它仍然可以通过简单地回退到以前的行为(无实现继承)来避免菱形问题。

        【讨论】:

        • 我认为 Java 8 并没有真正摆脱钻石问题。如果 B:A、C:A、D:B 和 D:C 和 B 包含 A 的方法之一的默认实现而 C 没有,那么该实现可以在 D 中使用,但据我了解它会然后是 C 的一项重大更改,以添加该方法的默认实现。
        • @supercat:好点。根据您的观点,您可以说 Java 8 确实 存在菱形问题,但只会使复杂情况出现编译时错误,以便更容易推理该行为(相比之下,例如, C++)。
        • 我认为它们不一定是编译时错误,因为 C 的默认实现可以在使用 D 编译的时间代码和运行代码的时间之间添加。我不记得Java在那种情况下做了什么,但是对于这种情况,编译了D的实现,A添加了一个成员,编译了使用新成员的D的消费者,然后使用默认实现编译了B。那时,D 的实现和消费者将起作用。但是,如果 C 是使用默认实现编译的,我看不到 JVM 有什么好的做法。
        • @supercat 仅供参考: 如果 B 和 C 都具有默认实现,并且 D 没有覆盖,JVM 将抛出 IncompatibleClassChangeError: Conflicting default methods。这既是编译错误,也是运行时错误。
        • @Andreas:我想知道在父接口中声明的方法中添加默认实现的频率是多少?如果只能将默认实现添加到声明方法的接口中,那将避免致命的钻石问题。另一方面,能够为接口方法指定多个默认方法重载并将Java绑定到实现类所满足的第一个,或者更进一步,允许接口指定任何可以满足其所有方法重载的类都应该是...
        猜你喜欢
        • 2012-05-08
        • 2016-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-17
        • 2017-03-24
        相关资源
        最近更新 更多