I have been trying to understands Mixins and I found on Wikipedia that Mixins can be used to avoid the diamond problem. Link
How the Diamond problem is avoided by using Mixins (Or Interfaces in java)
CodePudding user response:
That wikipedia article certainly needs some love at the time I'm writing this answer.
Given that you tagged in java, specifically:
- "The diamond problem" is a misnomer; there are a great many issues that are related to multiple inheritance of all stripes, and java merely avoids a number of them; no language that has any form of multi-inheritance could possibly solve them all.
- Presumably the key issue with diamond, at least as far as java is concerned, are about the sense in which java differs from C in this regard, as way back when java was originally developed, most of the language design decisions were of the flavour 'we just did what C( ) did', or 'we decided to deviate, because [commonly accepted issue with what C( ) does here]'. Diamond was one of them. Specifically: "unlike C , java does not allow extending more than one class, because [various issues generally referred to as the diamond problem]'.
- That means, specifically, ordering: If you have both
Parent1
andParent2
as parent types (be it because of a hypotheticalextends Parent1, Parent2
or be it becauseextends Parent1 implements Parent2
orimplements Parent1, Parent2
- those last 2 are valid in java, and both Parent1 as well as Parent2 have an identical method (say,void method()
), then which implementation 'wins'? - Java employs the maxim of: order should never matter. In other words, if Parent1 and Parent2 both offer implementations for the same method, in C , the first listed class 'wins'. In Java, you can't inherit from 2 classes so the point is moot. You CAN inherit multiple interfaces but as ordinary (i.e. without default methods) interfaces don't bring implementations, there's no issue whatsoever here: Yes, if you implement
Parent1
orParent2
(both interfaces in this example), you must write an implementation forvoid foo()
. If you implement them both, you... must write an implementation forvoid foo()
. No problem there.
Thus, let's continue by defining 'the diamond problem' as "Avoid having the ordering matter". Furthermore, as it is java, you still can't extend more than one thing, so we're really just talking about the ordering of interfaces. If you write implements Parent1, Parent2
, is there any change whatsoever if you edit that source file to read implements Parent2, Parent1
instead? In C the answer: "Yes, that matters". In java, the answer is supposed to be: "No, that is a meaningless change". Let's see how java has 'solved' the problem given that default methods now exist in java:
extends Parent1 implements Parent2
Given:
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 {}
Then this will compile, and new Foo().foo();
would print Parent1.foo
. That's because in java, the class hierarchy wins: The interface merely specifies a default implementation, to be used if no other impl is available. There IS another available (from Parent1
), thus, it wins.
Whether you consider this a 'fix' to the diamond problem depends on how you define the term 'Diamond Problem'. There is no one set definition that is universally agreed upon.
implements Parent1, Parent2
Given:
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 {}
then the above does not compile. Javac detects that there is a conflict (2 competing implementations of the same method), and as order must never matter, java CANNOT give one of the two preferential treatment just because it's listed first.
Hence, instead, javac will just error. You can fix it by writing an impl for that method explicitly. You can even choose one (or both!) of the parent impls:
class Foo implements Parent1, Parent2 {
@Override public void foo() {
Parent1.super.foo();
Parent2.super.foo();
}
}
Now new Foo().foo()
would print:
Parent1.foo
Parent2.foo
Hence, even with default methods in java now being a thing (which is exactly what a "mix-in" is, so even though that wikipedia doesn't list java as a language that has mix-ins, the wikipedia page is wrong, and in fact later says so in the section about java!) - order still does not matter.