MI, strictly speaking, is unnecessary. Also, it introduces a potential pitfall into the language that can be resolved two ways, each being equally inelegant--that is, from a design standpoint, there is no good reason to choose one way over the other, so it's basically an arbitrary decision of the language.
I refer to the so-called dreaded "diamond problem". Consider the following:
What does running Sub12's main method do? Will it print "Base" or "Sub1"...what implementation of the foo() method does Sub12 inherit: Sub1's, which overrides Base's implementation, or Sub2's, which simply inherits it and passes it down? (If you draw out the inheritance hierarchy in UML, you'll see why it's called the diamond problem.)
The fact is, OO experts have made weak arguments for a couple of decades why one approach is better than the other, mostly based on their personal preference, but the fact is that the choice between what Sub12 should do is equally well (badly?) justified either way. This is, of course, not a showstopper and a slam-dunk argument against MI, but it is an indicator of things to come--if the language itself can't even behave in an expected, deterministic fashion when presented with a very simple case of object inheritance hierarchies converging, how can this feature stand up in large, complex software projects?
There are better arguments to be made against MI, all of them more involved. To understand what I think is the most compelling one of these is too long to lay out here in full form, and requires you to really understand the difference between an abstract class and an interface (even a pure abstract class, that happens to have no method implementations). That difference, briefly, is that an interface says more, design-wise, than a pure abstract class in terms of how the project is going to evolve. When an abstract class is extended, the developer is (unwittingly, perhaps) agreeing that any method implementations added to that abstract class ought to be inherited by the subclass, no matter how bizarre they may be, no matter how many versions later those implementations are added. This binds a subclass to an abstract class in a very tight and intimate way, placing all sorts of extra restrictions on the assumptions it can make about the contract of the abstract class.
When a class implements an interface, on the other hand, it is assured that there will never be anything inherited from that interface except what it's getting right now. If the interface designer goes back and adds another method or changes an existing method signature, the compilation will break, unlike the abstract class if the developer adds a method implementation.
So if the relationship between a subclass and an abstract class is so intimate and so tightly coupled, is it better to have classes implement interfaces or extend abstract classes? Obviously interfaces is, as a rule, a hallmark of better design. So why, then, would it be a good idea to enable classes to extend not just one abstract class (or worse, and more tightly coupled, a non-abstract class!), but any number of them?
There are better alternatives. Several of design patterns address this very problem. Also, it is possible to achieve all the benefits of MI using a system of separating class APIs from class implementations and using aggregation, and this approach avoids the dreaded diamond problem.
You might say, wait, how often would the diamond problem happen in Java if Java allowed MI? Just do a good design and don't let it happen! This is nearly impossible, because every class in Java is a subclass of java.lang.Object, all MI would result in this diamond, meaning that you cannot override any of Object's methods in classes that are to be used in an MI hierarchy without encoutering it. Some say the Java compiler could just recognize the conflict and force you to implement it either as:
...or...
I think this just transfers a lot of code bloat--one of the main selling points of MI is that it removes code bloat, so by introducing it it's working at cross-purposes with one of stated goals of the language.
Now consider a situation where it seems like a good idea to use MI, though, and question whether it cannot be done another, better way. Let's say you have classes Base1, Base2, and Base3 and you want to implement a classes Sub1, Sub2, and Sub3, each of which extends all of Base1, Base2, and Base3. In other words, if MI were an option, you could write:
What are the advantages of inheritance that you're trying to exploit by using MI here? There are a few big ones as far as I can see. When a class Sub extends another class Base:
objects of type Sub can be polymorphically treated as objects of type Base. changes in method bodies in Base are *automatically* inherited by Sub, without having to update code in Sub. if ref instanceof Sub == true, then ref instanceof Base == true as well. Sub can override methods in Base. If a class Sub extends another class Base, then it meets all of the above criteria.
Many people will probably reply to your question with the age-old answer, "Prefer aggregation to inheritance". It's true that you can draft a good design that will use aggregation in place of MI in most cases. However, in some cases, you might prefer MI because you want *all* of the above conditions to be true for Sub wrt Base1, Base2, and Base3. In those cases, simple aggregation will not get you that:
In the above example... Can Sub be polymorphically treated as type Base? No. Are changes to foo() in Base1 automatically available to Sub without changing code in Sub? Yes. If some reference (ref instanceof Sub) == true, will (ref instanceof Base) == true? No. And finally, can Sub "override" methods of Base? Kind of--not actually "overriding" technically, but if I were to replace Sub's implementation of foo() with some other code, then that would approximate overriding Base's foo().
Still, though, we're left sorely lacking on the first and third points, so in cases where those points are important aggregation alone isn't quite cutting it. Worse yet, what if I want to simulate MI in not just one subclass, but three: Sub1, Sub2, and Sub3? Do I have to write this aggregation code for each and every one of them? Isn't there some way we can provide an easy means for other developers to extend these three Base classes easily? Is there any way we get all of the advantages of direct MI in Java through design?
I believe yes, but it requires separating Base1, Base2, and Base3's contracts (APIs) from their implementation classes. Once you do this, you can provide a single class that serves as a code container for all of the aggregation code. You can look at this as a sort of manual way to do MI for the Java compiler:
Notice I've marked the AbstractSub class protected (at the class level). This won't compile (classes can't be protected in Java), but it makes the point that no one but subclasses ought to "know" about that class. This is because I don't ever want code in the system to deal with objects polymorphically as type AbstractSub--instead, all subclassees of AbstractSub can be polymorphically treated as type Sub, the interface type (AbstractSub's docs should spell this out explicitly). AbstractSub therefore is specified only for one purpose: to act as a code container for subclasses to inherit all of that messy aggregation code. This is why I provided the Sub interface above, even though it is not strictly necessary (it could be deleted and AbstractSub could have simply implemented Base1, Base2, and Base3 instead): it provides a way for other code to treat Sub1, Sub2, and Sub3 polymorphically without having to decide between Base1, Base2, and Base3. What if a client wants to create an array of subs and call foo(), bar(), and baz() on each element without a bunch of messy typecasting? Can't be done without the Sub interface--since that's there, though, one could simply write:
Now let's go through the check list to see if we've hit all the points:
Can Sub1, Sub2, and Sub3 all be treated polymorphically as type Base1, Base2, and Base3? Yes.
Will changes to method bodies in any of the Base classes be automatically inherited by Sub1, Sub2, and Sub3? Yes.
If (ref instanceof Sub) == true, is (ref instanceof Base1 && ref instanceof Base2 && ref instanceof Base3) == true? Yes.
If Sub1, Sub2, or Sub3 provide foo(), bar(), or baz(), will these methods override the Base implementations? Yes.
Because all of these conditions are satisfied, we can consider any class that extends AbstractSub as having multiply inherited from Base1, Base2, and Base3 in every way.
One of these days, when I get time I'm going to see if there's a good way to implement this MI stuff using Java 1.5's new metadata facility, or maybe generics. Probably not very useful, but loads of fun.
sev