This page contains an archived post to the Design Forum (formerly called the Flexible Java Forum) made prior to February 25, 2002.
If you wish to participate in discussions, please visit the new
Artima Forums.
Message:
Inheritance, composition, and interfaces
Posted by Pierre-Antoine Champin on 02 Dec 1998, 4:02 PM
I received this e-mail from Pierre-Antoine Champin, which I thought everyone here might find interesting. I'm posting it here with Pierre-Antoine's permission. - Bill Venners I just read your last two articles in JavaWorld, with great interest. Nevertheless, I don't *quite* agree with you on a few points I would be happy to have your comments on... Not to get confused, when I'll talk about inheritence, it will be in a conceptual, language independant point of view. I will write "java-inheritance" when I speak about the language-specific feature. *** First I am a bit confused by your considering together composition and inheritance; and well, an Apple with an attribute Fruit seems to me a bit (don't mind me writing) anti-object, isn't it ? I was reliefed when I finally read that inheritance stands for an "is-a" relationship an ONLY in that case (not to get code re-use alone nor polymorphism alone). But that is so true to me that I didn't even thought of it as a problem... but then again you raised some interesting problems: - inheritance provides 'week encapsulation', which makes subclasses (and code using them) very affected by inteface modification in the superclass. But that problem still exists with the use of Interfaces. And what about maintaining both interfaces (in all classes or only subclasses, depending on the needs) of 'deprectaded' API's - which is the solution Sun uses between differents versions of Java. Another point is: in your example of the Fruit and the Apple, with composition, you can alter Fruit's implementation without modifying the Example application. But if you have many subclasses, you'll have a lot of code to change, which inheritance tends to avoid - that's the point of code re-use too. - the is-a relationship can vary in the time : I would say then that the choice of the classes is not good : if, in an application, a Person which is en Employee can become unemployed or if the application may use Employee's methods without using the Person instance behind the Employee, then I'd rather use a class Job (with a composition relation with Person) than a class Employee. - this problem you didn't raise, and I've been thinking of it for a time without ever reading something about that; I call it "inheritance insertion". What if I have classes Dog and Bird inheriting a class Animal, and that for some reason I want to add a class Cat. Cat would inherit Animal, but that would be great to have a class Mammal instead, inheriting Animal and inherited by both Cat AND Dog... If I have the source code of Dog, I may have a lot of cut-paste work... If I don't, then all I can do is write to the author... If all that happens at runtime, then this is even more itchy ! Your approach of replacing inheritance with composition may give me some way of solving that problem... but once again that looks kind of anti-objects ! May be too the magical package java.lang.reflect may help in some way - adding at least an INTERFACE Mammal to classes Cat and Dog? - but I didn't have time to check... *** As you point it, there are 2 aspects in inheritance : - code re-use, achieved by composition or java-inheritance - polymorphism, achieved by java-inheritance or interface Java inheritance allows both, or polymorphism alone with abstract classes, but Java classes, unlike interfaces, don't allow multiple inheritance - and that's a point I still don't get. I sure know about the diamond problem (say B1->A, B2->A and C->B1,B2), providing ambiguity in interface (B1 and B2 having identical methods) and implementation (does A's implementation occur once or twice in C). But I think - maybe naively - that the compiler could force the solving of interface ambiguity (just as it force the implementation of abstract methods in non abstract classes), or that a convention could be decided (based on the order of the superclasses, like in Python, by example). And for the implementation problem, if inheritance is a strict 'is-a' relationship, then A's implementation SHOULD occur once in C. Someone could argue that consistence in A's implementation could be broken by concurent use of B1 and B2; but that kind of critical implementation should normally be private and its consistence be checked entirely inside A itself. You bring as argument that Java philosophy is to separate interface from implementation, which is good. In other words (pardon me if I betray your words), Java is more concerned in polymorphism than in code re-use. But then why even providing the 'extends' keyword? Everything could be done by only implementing interfaces. You wrote you never used multiple inheritance in C++, which surpised me a bit. May be it's because I used C++ after I learned Java, but I did use multiple inheritance and polymorphism in C++ a lot, and I appreciated much code re-using - even if I fancy java much more than C++ in the end ;) On the other hand, I'm writing a Java application where I have had to write some artificial class Foo_Implementation, which I instanciate as an attribute, just to avoid copying entire methods in several classes implementing interface Foo in just the same way. Again, it's a good thing to make Foo an Interface, so it can be implemented in many different ways by different classes - I agree with that. But that would be good too to allow different classes to INHERIT some TypicalFoo class and avoid a lot of copied code which makes coding AND maintainance painful. That's all folks ! I hope you didn't mind me writing so long, but I'm deeply concerned and interested in the subject. thanks for reading Pierre-Antoine Champin
Replies:
|