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,因为 [各种问题通常被称为菱形问题]'。
- 这意味着,具体来说,排序:如果您同时拥有
Parent1
和 Parent2
作为父类型(可能是因为假设的 extends Parent1, Parent2
或者是因为 extends Parent1 implements Parent2
或 implements Parent1, Parent2
- 最后两个在 java 中有效,并且 Parent1 和 Parent2 都有相同的方法(例如,void method()
) , 那么哪个实现 'wins'?
- Java 采用以下格言:顺序无关紧要。换句话说,如果 Parent1 和 Parent2 都提供相同方法的实现,在 C++ 中,第一个列出 class 'wins'。在 Java 中,您不能从 2 class 继承,所以这一点没有实际意义。您可以继承多个接口,但作为普通(即没有默认方法)接口不带来实现,这里没有任何问题:是的,如果您实现
Parent1
或 Parent2
(本例中的两个接口) ,您必须为 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 并不重要。
我一直在努力理解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,因为 [各种问题通常被称为菱形问题]'。
- 这意味着,具体来说,排序:如果您同时拥有
Parent1
和Parent2
作为父类型(可能是因为假设的extends Parent1, Parent2
或者是因为extends Parent1 implements Parent2
或implements Parent1, Parent2
- 最后两个在 java 中有效,并且 Parent1 和 Parent2 都有相同的方法(例如,void method()
) , 那么哪个实现 'wins'? - Java 采用以下格言:顺序无关紧要。换句话说,如果 Parent1 和 Parent2 都提供相同方法的实现,在 C++ 中,第一个列出 class 'wins'。在 Java 中,您不能从 2 class 继承,所以这一点没有实际意义。您可以继承多个接口,但作为普通(即没有默认方法)接口不带来实现,这里没有任何问题:是的,如果您实现
Parent1
或Parent2
(本例中的两个接口) ,您必须为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 并不重要。