It’s drilled into the newbie Java programmers that Java (pre-Java 8) has no multiple class inheritance, and only multiple interface inheritance, because otherwise you run into diamond inheritance problem (Class A inherits from classes B and C, both of which implement method X. So which of those classes’ implementation is used when you do a.X() call?)
Clearly, C++ successfully addressed this, since it has multiple inheritance.
What’s different between the internal design of Java and C++ – (i’m guessing in the method dispatch methodology, and vaguely recall that it had to do with vtables from my long-ago computer languages class) that allows this problem to be solved in C++ but prevents in Java?
To rephrase:
What is the methodology of the language design/implementation used in C++ to avoid the ambiguity of diamond inheritance problem, and why couldn’t the same methodology be used in Java?
13
The fundamental difference is that Java defines conversions from a type to any of its supertype as being identity-preserving. This is a useful property to have, but it is incompatible with multiple inheritance. If d1
and d2
both derive from b
, and thing
is of a type which derives from both d1
and d2
, then in the absence of severe restrictions on what d1
and d2
can do, it’s possible that (b)(d1)thing
and (b)(d2)thing
should behave differently. If all casts from thing
to b
are required to go through d1
or d2
, then it’s possible for (b)(d1)thing
and (b)(d2)thing
to yield different objects, but that would imply that the casts are not identity-preserving.
Another difficulty which arises is that C++ is designed to be statically linked, which means that anything in the design of types d1
and d2
which would make use of common members ambiguous would be caught at link time. If the suppliers of d1
and d2
simultaneously add new features which are incompatible with each other, even though each would be compatible with the previous version of the other class, the person supplying the program that uses both would have to find a combination of classes that would work before the program could be linked, but consumers of the program would then be assured that the bundled versions of d1
and d2
would be compatible with each other.
In Java, it’s expected that different parts of a program may be upgraded separately. If the author of a class releases a new version, code which uses that class can be made to use the new version without having to involve the original programmer of the consuming code. A consequence of this is that if certain combinations of versions of different classes will work, but a few combinations will generate troublesome ambiguities, it’s likely that some such problems will only be discovered by a few unlucky end users who happen to have the exact troublesome combinations of versions.
While such versioning issues could arise in any case, multiple inheritance adds some kinds of failure which would be very hard to predict. If code is allowed to use a member which exists in one superclass but not the other without having to specify the superclass, then adding a like-named member to the other superclass would become a breaking change. Since the author of a class will generally have no idea with which other classes it may be combined, such an author would have no way of knowing what names were “safe” to use.
To be sure, some of the aforementioned problems can arise with Java’s new “default interface implementations” feature; the value of the feature was judged sufficient to justify accepting them. Still, it’s worthwhile to note that the dynamic linking of Java classes makes the tradeoffs very different from what they would be in a statically-linked language like C++.
14
There’s nothing fundamental in the internal design which causes this. The lack of multiple inheritance is a deliberate design decision in Java, not an external manifestation of a shortcoming in the internal design.
(I’m deliberately avoiding getting into the flame war as to whether multiple inheritance is a good idea or not).
8
Clearly, C++ successfully addressed this, since it has multiple inheritance.
Actually no, exactly the opposite. C++ did not successfully address this, and the diamond inheritance problem is a serious issue for C++ developers. There are techniques for dealing with it using “virtual inheritance”, but often they make things more complicated.
Java and other languages that came after C++ didn’t fail to implement multiple inheritance because they couldn’t solve it the same way C++ did; they learned from C++’s mistakes and didn’t implement a feature that introduces problems that are not easily solveable.
12
The diamond inheritance problem is not solved in C++, in the sense that C++ does allow you to shoot yourself in the foot this way.
The problem is solved in Java because a deliberate design decision was made to disallow multiple inheritance of implementations, thereby omitting the design element from which the problem arises.
The real problem with multiple inheritance is that its complexity outweighs its potential benefit. That’s principally why it was omitted from Java, not because of the diamond inheritance problem.
4
To answer the OP’s original question, there is a significant implementation feature in C++ not present in Java that allows multiple inheritance. The difference is that C++ must employ special tricks to fix up the ‘this’ pointer before entering a virtual function in a class with multiple concrete base classes. These tricks are vtable thunking and double-wide vtables.
C++ objects are arranged in memory as a contiguous block. Each derived class appends data to the block. Classes with a single inheritance chain start at one point in memory and all base classes and ancestors of the most derived class have the same ‘this’ pointer. Interfaces don’t have any data so they don’t affect the object layout. The ‘this’ pointer of all bases remains the same. This is true in C++, Java, C# and other languages that support objects with a single chain of concrete class inheritance along with any number of interfaces.
For a class with multiple concrete bases, only the layout of the first inheritance chain can be contiguous and coincident with the most derived class. The actual layout is implementation dependent and can become quite complex especially when there are several chains and several repeated copies of some base objects. In any case, the ‘this’ pointer changes depending on the point of view of each member function. This is not a problem for static or non-virtual functions, as the ‘this’ pointer is calculated at compile time. However, C++ must employ special tricks to fix up the ‘this’ pointer before entering a virtual function.
There were two popular strategies for this in the early days of C++. One was vtable thunking and the other was double-wide vtables. You can read more details about them in Inside the C++ Object Model by Stanley B Lippman. Essentially:
1) vtable thunking replaces the actual function address in the vtable with the address of a runtime thunk (an anonymous chunk of code) that adjusts the ‘this’ pointer then JMPs into the member function. The cost is a slight additional overhead for calling a virtual function in a class with multiple base classes.
2) double-wide vtables still store the member function address in the vtable but also the relevant ‘this’ pointer for that function (or a way to calculate it). The cost is that the vtable takes twice as much space for classes with multiple bases.
NOTE: Virtual Base Classes, including multiple virtual base classes, is an entirely different concept that uses an entirely different implementation. The common strategy for implementing VBCs is extending the vtable backward into negative offsets with pointers to the (not necessarily contiguous) data blocks of the virtual classes.
Of course, a class that has both virtual base classes and multiple concrete base classes will be quite a hairy monster indeed. I always marvel that C++ even compiles.
2
Both the Java and C++ underlying systems are powerful enough to allow/support multiple inheritance.
Neither system provides any baked in solutions (or other assistance) to help developers deal with the consequences/potential problems.
In C++, the conscious decision was made to expose as much of the power of the underlying system to developers as possible, and allow them to decide which subsets of the system are worth using, and which are not. A good part of the decision, initially, probably had to do with the initial compiler implementations not having to do much extra work to support multiple inheritance. The decision stuck.
As far as Java goes, see here for an early (1995) explanation. The explicit decision was made to prevent multiple inheritance from being possible, at the language level – it was deemed to be too much of a “foot gun”.
I might be misreading your question, but I’ll give it a go, because I suspect it is a question I also had at one point.
It might look like Java doesn’t have multiple implementation inheritance because it’s hard to implement, while C++ has because its implementors found a way to implement it. This isn’t the case. As others have said, it was a deliberate design decision to avoid a category of programming problems, not something to enable easier implementation of Java.
The very fact that Java has mulitple inheritance from interfaces means that its implementors have done the hard work of implementing multiple inheritance anyway – the fact that interfaces are abstract is mostly irrelevant.
In an implementation using vtables, to implement polymorphism under single inheritance, you need a vtable of the runtime type of your instance, perferably at a predetermined position. To implement polymorphism under multiple inheritance, you need multiple vtables.
For ease, let’s say that each type’s memory layout starts with its vtable (it’s not required to be like this, but it’ll make my description easier).
Type A [vtableA, mem1, mem2, ...]
Type B [vtableB, mem3, mem4, ...]
Type C : A, B [vtableA, mem1, mem2, ..., vtableB, mem3, mem4,...]
C* from C ^
Under single inheritance, you just pass the object reference around and you can always find the vtable of its runtime type.
Type C : A, B [vtableA, mem1, mem2, ..., vtableB, mem3, mem4,...]
A* from C ^
Under multiple inheritance, in order to use substitution, you have to pass a reference such that, if interpretreted as a reference to an instance of a supertype, it will still work. This means that the reference should point to another vtable in the memory layout of the whole instance. This is simply done by adding an offset, until the new reference points to the new vtable. This offset is known at compile time, and it’s probably the total size of the preceding data in the layout.
Type C : A, B [vtableA, mem1, mem2, ..., vtableB, mem3, mem4,...]
B* from C ^
<----memory size of A---->
Notice that in the schematics I used “type”, not “class”. If you assume that the language doesn’t allow you to declare your own data members, all the above still holds. The only difference is that Type B
will now consist of only a vtable (because the language won’t allow mem3
, mem4
, etc). But the mechanism is the same.
In C++ you can inherit from types that doesn’t share common ancestor and avoid the “dreaded diamond” problem altogether. In fact this is what most of C++ code do: design around the problem. When there is no other option, then one can choose between virtual inheritance or non-virtual one. Each has its own advantages and disadvantages, but none is a truly “successfully addressed” solution.
In Java, every class inherits Object
implicitly. Now this means that every multiple inheritance will create the “dreaded diamond” right away! Which means that even simple hierarchy would obligatory turn into a nightmare. This is where interface concept comes to the rescue. In C++ interface
is just another class. In Java, interface
is unique by not inheriting from Object
. Java solves the “dreaded diamond” problem in same way that 99% of C++ code does it: by not letting diamonds to happen in the first place.
Bottom line: introducing diamond inheritance to Java would help in very few cases at a huge cost of allowing very difficult to understand code.
You must keep in mind that the biggest advantage that Java holds over C++ is that it doesn’t let programmers to play with dangerous features that are mostly unnecessary (in jargon “shooting oneself in the foot”). Don’t ask “why we don’t have X”, ask yourself “which types of problems cannot be solved without X”. Most of the time you’d get a simple answer: “none”. Or “none that matter to overwhelming majority of Java users”.
As others have said I will question if C++ successfully addressed multiple inheritance.
(The only language I have seen that I consider successfully addressed multiple inheritance is Eiffel, but as we are not all using it, it is questionable if it was successfully. I expect that most programmers will find the way it does multiple inheritance too hard to learn as it takes more than 10 minutes thinking to understand it.)
However back to Java (and C#), in both cases it was a requirement that classes could be loaded at run time. And there is no known why to efficiently implement multiple inheritances if classes can be dynamically loaded.
I recall from the time when Java come out that this was the most common reason given by the people working on it in talks.
2