Why is the diamond case with its common ancestor used to explain Java multiple inheritance issue, instead of two unrelated parent classes?

点点圈 提交于 2020-01-01 07:45:50

问题


This question might sound weird to Java people but if you try to explain this, it would be great.

In these days I am clearing some of Java's very basic concept. So I come to Inheritance and Interface topic of Java.

While reading this I found that Java does not support Multiple Inheritance and also understood that, what I am not able to understand that why everywhere Diamond figure issue(At least 4 class to create diamond) is discussed to explain this behavior, Can't we understand this issue using 3 classes only.

Say, I have class A and class B, these two classes are different (they are not child class of common class) but they have one common method and they look like :-

class A {
    void add(int a, int b) {

    }
}

class B {
    void add(int a, int b) {

    }
}

Ok,Now say if Java supports Multiple inheritance and if there is one class which is the subclass of A and B like this :-

class C extends A,B{ //If this was possible
    @Override
    void add(int a, int b) { 
        // TODO Auto-generated method stub
        super.add(a, b); //Which version of this, from A or B ?
    }
 }

then compiler will not be able to find which method to call whether from A or B and that is why Java does not support Multiple Inheritance. So is there any thing wrong with this concept ?

When I read about this topic I was able to understand Diamond issue, but I am not able to understand why people are not giving example with three class (If this is valid one, because we used only 3 classes to demonstrate issue so its easy to understand by comparing it to Diamond issue.)

Let me know whether this example does not fit to explain issue or this can also be referred to understand issue.

Edit: I got one close vote here stating that question is not clear. Here is main question :-

Can I understand Why "Java does not support Multiple Inheritance" with 3 classes only as described above or I must need to have 4 classes (Diamond structure) to understand the issue.


回答1:


The problem with diamond inheritance is not so much shared behaviour but shared state. As you can see, Java in fact has always supported multiple inheritance, but only multiple inheritance of type.

With only three classes the problem is resolved relatively easily by introducing a simple construct like super.A or super.B. And while you're only looking at overridden methods, it indeed doesn't matter whether you have a common ancestor or just the basic three classes.

However if A and B have a common ancestor, the state of which they both inherit, then you're in serious trouble. Do you store two separate copies of the state of this common ancestor? That would be more like composition than inheritance. Or do you only store one that is shared by both A and B, causing strange interactions when they manipulate their inherited shared state?

class A {
  protected int foo;
}

class B extends A {
  public B() {
    this.foo = 42;
  }
}

class C extends A {
  public C() {
    this.foo = 0xf00;
  }
}

class D extends B,C {
  public D() {
    System.out.println( "Foo is: "+foo ); //Now what?
  }
}

Note how the above wouldn't be so big a problem if class A didn't exist and both B and C declared their own foo field. There would still be a problem of clashing names, but that could be resolved with some namespacing construct (B.this.foo and C.this.foo maybe, as we do with inner classes?). The true diamond problem on the other hand is more than a naming clash, it's a matter of how to maintain class invariants when two unrelated superclasses of D (B and C) share the same state they both inherit from A. This is why all four classes are needed to demonstrate the full extent of the problem.

Shared behaviour in multiple inheritance doesn't exhibit the same problem. So much so that the recently introduced default methods do exactly that. This means that multiple inheritance of implementations is allowed now too. There is still some complication around the resolution of which implementation to call but since interfaces are stateless, the biggest bugbear is avoided.




回答2:


Java does not support multiple inheritance because the designers of the language designed Java this way. Other languages like C++ support multiple inheritance just fine, so it is not a technical issue but just a design criteria.

The problem with multiple inheritance is that it is not always clear which method from which class you are calling to, and which instance variables you are accessing to. Different people interpret it differently and the Java designers believed at that time that it was better to skip multiple inheritance altogether.

C++ solves the diamond shaped class issue with virtual inheritance:

Virtual inheritance is a technique used in object-oriented programming, where a particular base class in an inheritance hierarchy is declared to share its member data instances with any other inclusions of that same base in further derived classes. For example, if class A is normally (non-virtually) derived from class X (assumed to contain data members), and class B likewise, and class C inherits from both classes A and B, it will contain two sets of the data members associated with class X (accessible independently, often with suitable disambiguating qualifiers). But if class A is virtually derived from class X instead, then objects of class C will contain only one set of the data members from class X. The best-known language that implements this feature is C++.

Contrary to Java, in C++ you can disambiguate which instance method to call by prefixing the call with the name of the class:

class X {
  public: virtual void f() { 

  } 
};

class Y : public X {
  public: virtual void f() { 

  } 
};

class Z : public Y {
  public: virtual void f() { 
    X::f();
  } 
};



回答3:


That is just one difficulty that you have to solve for multiple inheritance in a language. Since there exist languages that do have multiple inhertance (e.g. Common Lisp, C++, Eiffel), it is obviously not insurmountable.

Common Lisp defines the exact strategy for priorizing (ordering) the inheritance graph, so there is no ambiguity in the rare cases where it does matter in practice.

C++ uses virtual inheritance (I have not yet put the effort in to understand what that means).

Eiffel allows to specify exactly how you want to inherit, possibly renaming methods in the subclass.

Java just skipped multiple inheritance. (I personally think that it is difficult not to be insulting to its designers while trying to rationalize this decision. Of course, it is hard to think about a thing when the language you think in does not support it.)




回答4:


The diamond problem with four classes is simpler than three classes problem described in the question.

The problem with three classes adds another issue that has to be solved first: The naming conflict caused by two unrelated add methods with the same signature. It's not actually hard to solve but it adds unnecessary complexity. It would probably be allowed in Java (like it is already allowed to implement multiple unrelated interface methods with the same signature), but there could be languages that simply prohibit multiple inheritance of similar methods without a common ancestor.

By adding a fourth class which defines add, it is clear that both A and B are implementing the same add method.

So the diamond problem is more clear because it shows the problem using simple inheritance instead of just using methods with the same signature. It is well known and probably applies to a wider range of programming languages, so the variation of it with three classes (which adds the additional naming conflict issue) doesn't make much sense and therefore hasn't been adopted.




回答5:


Multiple inheritance is no problem if you find a sane solution to method resolution. When you invoke a method on an object, method resolution is the act of picking which class'es version of the method should be used. For this, the inheritance graph is linearized. Then, the first class in this sequence that provides an implementation of the requested method is chosen.

To illustrate the following examples of conflict resolution strategies, we will be using this diamond inheritance graph:

    +-----+
    |  A  |
    |=====|
    |foo()|
    +-----+
       ^
       |
   +---+---+
   |       |
+-----+ +-----+
|  B  | |  C  |
|=====| |=====|
|foo()| |foo()|
+-----+ +-----+
   ^       ^
   |       |
   +---+---+
       |
    +-----+
    |  D  |
    |=====|
    +-----+
  • The most flexible strategy is requiring the programmer to explicitly choose the implementation when creating an ambiguous class, by explicitly overriding the conflicting method. A variant of this is banning multiple inheritance. If a programmer wants to inherit behaviour from multiple classes, composition will have to be used, and a number of proxy methods to be written. However, naively explicitly resolved inheritance conflicts has the same shortcomings as…

  • Depth first search, which might create the linearization D, B, A, C. But this way, A::foo() is considered before C::foo() although C::foo() overrides A::foo()! This can't be what we wanted. An example of a language using DFS is Perl.

  • Use a clever algorithm that guarantees that if X is a subclass of Y, it will always come before Y in the linearization. Such an algorithm will not be able to unravel all inheritance graphs, but it provides sane semantics in most cases: If a class overrides a method, it will always be preferred over the overridden method. This algorithm exists and is called C3. This would create the linearization D, B, C, A. C3 was first presented in 1996. Unfortunately, Java was published in 1995 – so C3 was not known when Java was initially designed.

  • Use composition, not inheritance – revisited. Some solutions to multiple inheritance suggest getting rid of the “class inheritance” bit, and instead propose other units of composition. One example is mixins, which “copy & paste” method definitions into your class. This is incredibly crude.

    The idea of mixins has been refined into traits (presented 2002, also too late for Java). Traits are a more general case of both classes and interfaces. When you “inherit” a trait, the definitions are embedded into your class, so that this does not complicate method resolution. Unlike mixins, traits provide more nuanced strategies to resolve conflicts. Especially, the order in which traits are composed matters. Traits play a prominent role in Perl's “Moose” object system (called roles) and in Scala.

Java prefers single inheritance for another reason: If each class can only have one superclass, we don't have to apply complicated method resolution algorithms – the inheritance chain already is the method resolution order. This makes methods a bit more efficient.

Java 8 introduced default methods which look similar to traits. However, the rules of Java's method resolution make interfaces with default methods much less capable than traits. Still, a step in the direction of adapting modern solutions to the multiple-inheritance problem.

In most multiple inheritance method resolution schemes, the order of superclasses does matter. That is, there's a difference between class D extends B, C and class D extends C, B. Because the order can be used for simple disambiguation, a three-class example does not sufficiently demonstrate the problems associated with multiple inheritance. You need a full four-class diamond problem for that, as it shows how naive depth-first search leads to an unintuitive method resolution order.




回答6:


The diamond problem that you cite is one reason (and a very good one) not to support multiple inheritance. There are other reasons too, which you can read a bit about here.

A lot of the reasons boil down to complexity (it's quite complex to do it right), the relatively rare legitimate need to use it, and all sorts of other problems with dispatching (aside from just the diamond problem) that it creates.



来源:https://stackoverflow.com/questions/26339237/why-is-the-diamond-case-with-its-common-ancestor-used-to-explain-java-multiple-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!