MixIns(或Interfaces)如何解决菱形问题

How Does MixIns (Or Interfaces) solves the diamond problem

我一直在努力理解Mixins,在维基百科上发现Mixins可以用来避免菱形问题。 Link

如何使用 Mixins(或 java 中的接口)避免 Diamond 问题

在我写这个答案的时候,那篇维基百科文章当然需要一些爱。

鉴于您在 java 中标记,特别是:

  • “钻石问题”用词不当;有很多问题与所有条纹的多重继承有关,java 只是避免了其中的一些问题;任何具有任何形式的多重继承的语言都不可能解决所有问题。
  • 据推测,钻石的关键问题,至少就 java 而言,是关于 java 在这方面与 C++ 的不同之处,就像回到 java 最初开发时,大多数语言设计决策都具有 'we just did what C(++) did' 或 'we decided to deviate, because [commonly accepted issue with what C(++) does here]' 的风格。戴蒙德就是其中之一。具体来说:“与 C++ 不同,java 不允许扩展多个 class,因为 [各种问题通常被称为菱形问题]'。
  • 这意味着,具体来说,排序:如果您同时拥有 Parent1Parent2 作为父类型(可能是因为假设的 extends Parent1, Parent2 或者是因为 extends Parent1 implements Parent2implements Parent1, Parent2 - 最后两个在 java 中有效,并且 Parent1 和 Parent2 都有相同的方法(例如,void method()) , 那么哪个实现 'wins'?
  • Java 采用以下格言:顺序无关紧要。换句话说,如果 Parent1 和 Parent2 都提供相同方法的实现,在 C++ 中,第一个列出 class 'wins'。在 Java 中,您不能从 2 class 继承,所以这一点没有实际意义。您可以继承多个接口,但作为普通(即没有默认方法)接口不带来实现,这里没有任何问题:是的,如果您实现 Parent1Parent2(本例中的两个接口) ,您必须为 void foo() 编写一个实现。如果你同时实现它们,你......必须为 void foo() 编写一个实现。没问题。

因此,让我们继续将 'the diamond problem' 定义为“避免顺序问题”。此外,由于它是 java,您仍然不能扩展超过一件事,所以我们实际上只是在谈论接口的顺序。如果您编写 implements Parent1, Parent2,如果您编辑该源文件以改为读取 implements Parent2, Parent1,是否会有任何变化?在 C++ 中,答案是:“是的,这很重要”。在 java 中,答案应该是:“不,那是无意义的改变”。让我们看看 java 如何解决 'solved' 问题,因为 java:

中现在存在默认方法

extends Parent1 implements Parent2

给定:

class Parent1 {
  public void foo() {
    System.out.println("Parent1.foo");
  }
}

interface Parent2 {
  default void foo() {
    System.out.println("Parent2.foo");
  }
}

class Foo extends Parent1 implements Parent2 {}

然后这将编译,并且 new Foo().foo(); 将打印 Parent1.foo。那是因为在 java 中,class 层次结构胜出:该接口仅指定一个默认实现,如果没有其他 impl 可用,则使用该默认实现。还有另一个可用的(来自 Parent1),因此它获胜。

您是否认为这是 'fix' 钻石问题取决于您如何定义术语 'Diamond Problem'。没有一套被普遍接受的定义。

implements Parent1, Parent2

给定:

interface Parent1 {
  default void foo() {
    System.out.println("Parent1.foo");
  }
}

interface Parent2 {
  default void foo() {
    System.out.println("Parent2.foo");
  }
}

class Foo implements Parent1, Parent2 {}

那么上面的不编译。 Javac 检测到存在冲突(同一方法的 2 个竞争实现),并且由于 顺序绝对无关紧要 ,java 不能给出两者之一只因排在第一位而受到优待

因此,javac 只会出错。您可以通过显式为该方法编写一个 impl 来修复它。您甚至可以选择一个(或两个!)父 impls:

class Foo implements Parent1, Parent2 {
  @Override public void foo() {
    Parent1.super.foo();
    Parent2.super.foo();
  }
}

现在 new Foo().foo() 将打印:

Parent1.foo
Parent2.foo

因此,即使使用 java 中的默认方法现在也是一回事(这正是“混合”的意思,所以即使维基百科没有将 java 列为有混音的语言,维基百科页面是错误的,事实上后来在关于 java 的部分中是这样说的!)-顺序 still 并不重要。