What are the advantages of inheritance that you're trying to exploit by using MI? 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. If you have a situation where you need a particular Sub to have this relationship with Base1, Base2, and Base3 (very rare indeed), then you have a case of MI on your hands.
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.
However, you can effectively get MI in Java if you use a combination of a more complex form of aggregation and interfaces. Basically, you have to separate the interfaces of the classes from their implementations, and then aggregate all of the Base class implementations in the subclass and manually forward the calls. It's complicated, but it gets you a Sub that follows all of the above listed rules with respect to multiple base classes, and if you need it, you need it. Here ya go:
Notice I've marked the AbstractSub class protected (at the class level). This, of course, will not compile (classes can't be marked protected), but it gets across the point of what I'd like to do. Unfortunately, cuz you can't have a protected class, you'd have to go ahead and mark it public and just explain in the
doc that no one should ever, ever, ever create references of type AbstractSub--they should always use references of type Sub instead.
This is because I don't ever want code in the system to deal with objects polymorphically as type AbstractSub--instead, all subclasses of AbstractSub can be polymorphically treated as type Sub, the interface type. 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 one of the supertypes 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. Probably not very useful, but loads of fun I'm sure.
This is a mighty long post, so I might come back and address some of the other interesting points in this
thread in subsequent posts.
sev
[ April 11, 2004: Message edited by: sever oon ]