26

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?

DVK
  • 3,576
  • 2
  • 24
  • 28
  • 60
    `Clearly, C++ successfully addressed this, since it has multiple inheritance.` Clearly C++ implemented multiple inheritance; that doesn't mean it addressed its problems. C++ has no problem with you shooting your feet off. – Doval Dec 30 '14 at 19:00
  • 5
    @Doval - unless calling a.X() in case of diamond inheritance is an undefined behavior, I would say that it solved the problem. Now, whether it solved ALL the conceptual problems of multiple inheritance as an idea, is a different story, but it's not germaine to what I'm asking. – DVK Dec 30 '14 at 19:03
  • One could argue that Java allows [diamond multiple inheritence](http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html) too as of Java 8. –  Dec 30 '14 at 19:12
  • 2
    @Snowman - sorry, we are still on 6 here. I'll update the question to restrict to 1-7, but feel free to post what Java 8 added that covered the gap and what wasn't available before. – DVK Dec 30 '14 at 19:13
  • @DVK it is okay, Java 8 is the biggest language feature update in over ten years (Java 5 was release Sep 30, 2004) and includes some pretty big changes. I understand that most legacy code is still on older versions, which is why unless the question specifically states Java 8 I assume it is about 5/6/7. –  Dec 30 '14 at 19:17
  • 1
    @DVK You say it's not germane, but the diamond problem is purely a conceptual problem. The ambiguity can be resolved by using [C3 linearization](http://en.wikipedia.org/wiki/C3_linearization), consulting an oracle, or even just not dealing with the issue and throwing a runtime exception, all of which are perfectly defined (though not necessarily deterministic) behaviors. The point being that there's nothing about the design of Java that prevents it; the designers simply felt it was a feature that was too easy to misuse. – Doval Dec 30 '14 at 19:20
  • @DVK - it's extremely difficult to give you an appropriate answer to this question. The trivial answer is that, because Java uses dynamic dispatch, there is nothing physically standing in the way of supporting multiple inheritance. However, it's unclear whether you understand how dynamic dispatch works (even though you mention v-tables), so a real answer would need to expand on how the compiler translates a method call into an indirect lookup. Or, at the other end, there are performance implications depending on the ordering of methods ... – kdgregory Dec 30 '14 at 19:25
  • ... which is why Java has distinct `invokevirtual` and `invokeinterface` operations (as well as several other forms of method invocation). – kdgregory Dec 30 '14 at 19:29
  • @kdgregory - yes, I specifically expected a decent asnwer to delve to the level of detail that your second-to-last comment mentions. "because they could but chose not to" isn't an answer, it's handwaving. Also, "whether you understand" is - leaving aside being a bit on the rude side - fully irrelevant to how SE works. A good answer is there for 100% of present and future readers, not only the OP. – DVK Dec 30 '14 at 19:33
  • 1
    Could you be more specific about what you mean by "diamond inheritance", with specific reference to the difference between virtual and non-virtual inheritance in C++? The "solution" to diamond inheritance in C++ varies a lot on whether you've got virtual inheritance or not (or in horrible cases, both). – Philip Kendall Dec 30 '14 at 19:56
  • 9
    Java also doesn't support integer types of explicit length (`uint32_t`, etc), preprocessing macros, raw pointers and explicitly allocated memory, in each case, a deliberate conscious decision about what the language should be. Just because a language doesn't implement something doesn't mean that the designers didn't know how. – Gort the Robot Dec 30 '14 at 22:33
  • 11
    In C++, programmers (are forced to) deal with the diamond inheritance problem. And "because they chose not to" isn't handwaving--it's how language design works: languages always include and exclude features at the discretion of their designer(s). Python, for instance, does not include increment/decrement operators; this has nothing to do with "internal design." – Kyle Strand Dec 30 '14 at 23:23
  • 2
    @StevenBurnap all Java integer (byte, short, int, long) types have explicit lengths. – Tyilo Dec 31 '14 at 20:54

9 Answers9

69

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).

Philip Kendall
  • 22,899
  • 9
  • 58
  • 61
  • Unless Java has an equivalent of vtables, I'm pretty sure this answer is wrong. – DVK Dec 30 '14 at 19:15
  • 10
    Java does have vtables... (Or perhaps an equivalent design). Just think about it. Methods is virtual by default in that language. – Thomas Eding Dec 30 '14 at 19:16
  • 22
    One could even say that Java is vtables all the way down. – kdgregory Dec 30 '14 at 19:17
  • More importantly you never need to know about vtables in java, if you do there is the java.lang.class to get a reference to the Method if needed. – ratchet freak Dec 30 '14 at 19:39
  • 16
    @DVK Nowhere are vtables mentioned in the C++ standard either. – fredoverflow Dec 30 '14 at 21:36
  • 9
    Stroustrup designed the language knowing that vtables would be an efficient way to implement since the whole point of C++ was to keep the cost of OO light. But vtables are merely one way to solve the problem and whether Java uses them is completely irrelevant to this question. Java could easily have allowed multiple inheritance if the designers had desired, and indeed has interfaces, which are a sort of restricted multiple inheritance. – Gort the Robot Dec 30 '14 at 22:27
  • I wouldn't say C++ failed, there isn't really a "solution" (it cannot be well defined, this is known) but it gives you the tools to deal with it. This is bordering a formal language argument but there is no "solution", take for example Set Theory and Russell's paradox, it doesn't mean you can't have sets if you choose axioms that prevent it you loose a lot of convenience, it's the same sort of thing. Anyone with a formal background wanna back me up? – Alec Teal Dec 31 '14 at 14:38
  • 12
    The general philosophy of Java is to make certain common errors impossible. The general philosophy of C++ is to allow the developer to do anything they want, even at the risk of creating certain common errors. Both languages succeed in implementing their philosophy. – Gort the Robot Dec 31 '14 at 18:38
42

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.

Mason Wheeler
  • 82,151
  • 24
  • 234
  • 309
  • 8
    Actually, C++ gives you the choice between diamond and non-diamond inheritance, so this is not really the problem. Note that most languages which provide MI (eg. python, lisp) are very dynamic in nature, and allow you for instance to customize the way methods overrides are resolved. In C++, multiple inheritance is actually very crippled if you compare it to eg. lisp's CLOS with its multiple dispatch mechanism, and this reduces its usefulness to interface inheritance, à la Java. – Alexandre C. Dec 30 '14 at 22:19
  • 6
    @AlexandreC.: Yes, but then you've got *dynamism* to deal with. That's like dealing with a pain in your foot by amputating everything below the knee. Sure, the pain in your foot is now gone, but it's hardly what a reasonable person would consider "a good solution"! – Mason Wheeler Dec 30 '14 at 22:34
  • 2
    @MasonWheeler - you seem to be forgetting Phantom Limb Syndrome... – Boris the Spider Dec 30 '14 at 22:36
  • 4
    "Serious problems". Good grief. The diamond is the best default behavior; if you think about it, it really is sensible unless you have poor design methodology to begin with. People taut about virtual inheritance, and IMO it is not needed much. Duplicating privates makes sense. – Thomas Eding Dec 31 '14 at 06:09
  • 1
    @ThomasEding it might be the best solution, but it's still a confusing solution. It does make a bit more sense if you think of inheritance as syntactic sugar for composition. – user253751 Dec 31 '14 at 06:13
  • 4
    How often is multiple inheritance used? Sometimes I use some pure virtual classes (essentially equivalent to Java Interfaces), but that's about it. Saying the Diamond Problem causes "serious problems" seems overdramatic - it practically never comes up. – Kevin Dec 31 '14 at 07:35
  • 2
    @MasonWheeler: well, surely nobody is going to choose a dynamic over a static language _just_ because it supports multiple inheritance. But it's certainly a valid point to remark here that problems like Diamond can be better solved in dynamic languages (how true that is I'm not actually sure), whereas general flamewar about dynamic vs. static is hardly on topic here (question already has enough Java vs. C++ flamewar). – leftaroundabout Dec 31 '14 at 08:59
  • 5
    Saying multiple inheritance is a mistake in C++ shows a real lack of knowledge of how it is used in modern C++ design. Multiple inheritance makes the things you can do with templates dramatically more powerful. – Jack Aidley Dec 31 '14 at 10:33
  • 3
    @JackAidley: Templates. So it's useful because it amplifies the foot-shooting gun into a leg-blower-offer? – Mason Wheeler Dec 31 '14 at 13:19
  • 5
    @KevinMills How do you know the reason it never comes up isn't because people know it's a problem and therefore avoid it? You could just as easily say `goto` doesn't cause serious problems because it practically never comes up (or `malloc`/`new`, or any other feature or practice that general concensus says is problematic.) In practice learning C++ really means learning all the ways to avoid misusing its features. – Doval Dec 31 '14 at 14:15
  • @JackAidley: How often is MI useful in cases where the base class would have no reason to be aware of all but one of the "joined" subclasses? If I were designing an OO framework, I wouldn't allow "general" MI, but would allow a class (e.g. `BaseFoo`) to define nested subclasses which a derivative of `BaseFoo` (e.g. `DerivedFoo`) could either support or disable; an enabled nested subclass of `DerivedFoo` would then be substitutable for either a `DerivedFoo` or its corresponding subclass of `BaseFoo`, even though the latter two types were unrelated to each other. – supercat Dec 31 '14 at 21:04
  • @JackAidley: While the way a class is implemented will often limit the kinds of things for which it may be substituted, I would posit that in many cases the what-is-substitutable-for-what relationships should be somewhat orthogonal to what-is-implemented-using-what relationships, but I've not seen languages really express that. – supercat Dec 31 '14 at 21:15
27

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++.

Martijn Pieters
  • 14,499
  • 10
  • 57
  • 58
supercat
  • 8,335
  • 22
  • 28
  • 3
    The point about version-related incompatibilities is interesting; many MI-implementations would indeed have considerable problems there. However, your first paragraph about non-identity-preserving casts is only relevant for a specific implementation technique for inheritance (linear VTables). E.g. name-based dispatch mechanisms such as hash tables make casts unnecessary, but generally have worse performance. – amon Dec 30 '14 at 21:47
  • 4
    @amon: The problem isn't one of implementation, but semantics. Java guarantees that if `b r1, r2, r3; r1=(b)(d1)thing; r2=(b)(d2)thing; r3=(b)thing;``, compiles, then `r1`, `r2`, and `r3` will be equivalent. Unless members of `b` inherited through `d1` are synonymous with those inherited from `d2` (which would imply that neither `d1` nor `d2` could provide an override which was usable by a derived class), such an equivalence could not hold without violating some other common expectations of objects. – supercat Dec 30 '14 at 22:06
  • 1
    Isn't the fact that Java8 actually does have MI while still being identity preserving a contradiction with "This is a useful property to have, but it is incompatible with multiple inheritance"? – Voo Dec 31 '14 at 11:28
  • @supercat In more recent MI languages (such as Python and Scala), it is possible for `d1` and `d2` to both provide an override. Any class that inherits from both must specify the order of precedence. One consequence of this is that you can no longer assume that the method inherited from the superclass is the method inherited from `b`. In C++ this would be a violation of expectations, but in Python or Scala this would be expected, and is occasionally useful. – James_pic Dec 31 '14 at 12:26
  • @voo java 8 default method implementations aren't really multiple inheritance in the traditional way: the methods aren't really inherited from the interface, but are implemented in the class derived from it with an implementation copied out of the interface. This has a number of effects, most importantly that multiple methods with the same signature get merged into a single implementation, rather than having the possibility of ending up with multiple different implementations as you can with c++. – Jules Dec 31 '14 at 14:42
  • @Jules Not sure I follow. Say you have interfaces A and B both having a default implementation of method `foo`, then you can still call both foo methods you just have to indicate which one you want. The only difference I see is how the compiler decides which method to call - in Java you have to be more explicit than in C++ in some cases. – Voo Dec 31 '14 at 14:45
  • @Voo: If interface `I1` includes method `foo()` but does not define an implementation, and interfaces `I1a` and `I1b` both derive from `I1`, with only `I1a` supplying a default implementation, having `C1` implements both `I1a` and `I1b` but not define `foo` would be perfectly legitimate; if `I1b` were changed to also supply a default implementation, that would create a deadly diamond. I'm not sure how Java decides what to do there, but having it select an implementation arbitrarily would hardly be the most evil thing in the world, since default implementations... – supercat Dec 31 '14 at 20:43
  • ...are generally used in cases where it's expected that they may be independently overridden, to a much greater extent than virtual methods in general. Although .NET doesn't have a mechanism to allow default method implementations, it could probably do so somewhat more safely than Java if it required that default implementations appear within the interface that defined the original member [as opposed to inheriting it]. – supercat Dec 31 '14 at 20:46
  • @James_pic: I wish more languages would allow prioritization not just of overrides, but also overloads; I don't like the idea of allowing a class to inherit two supertypes on "equal terms", but allowing a class to have one primary supertype as well as multiple things for which it is substitutable is certainly helpful and would avoid the "identity" issue I mentioned. If `Moo` and `Boo` both derive from `Foo`, `Moo` is the primary supertype of `Goo`, and `Boo` specifies that it should be usable as a secondary supertype, then `Goo` should be able to inherit `Boo` as a secondary supertype. – supercat Jan 01 '15 at 18:30
  • @James_pic: I think being able to specify that a type should *only* be usable as a primary supertype would be helpful, since a primary supertype `Moo` would be entitled to assume that if its method `Bar` was overridden, and that override called `base.Bar`, that call would go through `Moo.Bar`. For some types, such a guarantee may be very important [btw, I wish there were a way of specifying that a method may only be overridden by a method which chains to base exactly once on any non-exceptional path]. For other types, such a guarantee wouldn't have value. – supercat Jan 01 '15 at 18:34
  • @supercat You may like Scala's inheritance model. It has "traits" that support multiple inheritance, and should be written with co-operative inheritance in mind, and "classes" that can only be singly inherited. I think this was primarily due to limitations in the JVM, but I believe some other languages take a similar class-mixin separation approach - I think Ruby does something similar – James_pic Jan 02 '15 at 09:42
  • @James_pic: I'll have to look into those; I would think that a language which compiles to JVM bytecode (I think Scala does, right) would be limited by what the JVM can do, but a language could go beyond if it had a few conventions which types should follow, and limited use of certain features to types obeying such conventions. For example, one could define an interface `UseableAs` with a member `T useAs()` which would in all "legitimate" implementations return `this`. There would be no mechanism in the JVM to prevent a type from implementing that interface to do something else... – supercat Jan 03 '15 at 17:57
  • ...and a type which did so might behave very oddly, but such an approach could be pretty powerful otherwise. – supercat Jan 03 '15 at 18:01
  • @supercat Scala implements traits as interfaces at the JVM level (which already support multiple inheritance, but without implentations). It handles method implementations on traits by having the compiler implement the methods in any concrete subclasses, much like Java 8. Interestingly Scala also implements something akin to your `UsableAs` proposal with implicits - and you're right, it's powerful but sometimes difficult to reason about. – James_pic Jan 05 '15 at 09:42
17

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.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • I distinctly remember my Languages CS class professor stating the contrary, though for the life of me I don't recall the reasoning since I haven't done C++... well, ever, since college. – DVK Dec 30 '14 at 19:01
  • 18
    The diamond "problem" is solved in C++ in the sense that one can (and thankfully must) disambiguate ambiguous member access. It also allows for virtual inheritance when needed. – Thomas Eding Dec 30 '14 at 19:14
  • 1
    Some languages use precedence rules to overcome the diamond problem. Namely Python, Scala. What are your thoughts on this approach ? – Amogh Talpallikar Dec 31 '14 at 06:09
  • @AmoghTalpallikar: I'm not familiar with those languages, but the principle of having one *primary* base class while allowing other secondary classes would seem useful, especially if classes can independently allow or disallow primary and secondary inheritance. – supercat Nov 11 '16 at 14:13
15

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.

  • 1
    But there is no reason that Java couldn't have used such approaches. – Jack Aidley Dec 31 '14 at 10:28
  • 4
    It’s worth pointing out that what you describe here is a common implementation strategy, but there’s nothing that says C++ objects have to be designed this way. Also note that even for single inheritance, the vtable pointer (if one is used) will usually be changed during construction and destruction time to reflect which base object is currently existing. – Christopher Creutzig Dec 31 '14 at 10:44
11

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".

blueberryfields
  • 13,200
  • 8
  • 51
  • 87
1

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.

1

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".

JDługosz
  • 568
  • 2
  • 9
Agent_L
  • 387
  • 1
  • 7
0

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.

Ian
  • 4,594
  • 18
  • 28
  • as far as I can tell, this seems to merely repeat points made and explained in [this prior answer](http://programmers.stackexchange.com/a/267689/31260) (there is even a reference to historical documentation of "time when Java come out" over there, not just vague recollection) – gnat Dec 31 '14 at 10:56
  • That answer talks about the risks of the “foot gun" with multiple inheritance, it was also a real issue that at the time that no one know a good way to implement it with run time loading of classes. – Ian Dec 31 '14 at 11:12