Multiple Inheritance Is Not Evil

OO practitioners often deprecate MI, but there are two situations where it's invaluable:

I used MultipleInheritance once and it blew my arm off. Not just my hand but my entire arm, just gone!

The problems that are attributed to MI are more properly put down to its facillitating sloppy factoring; if you use some decent design discipline you'll find that there's zero inherent evil in MI, as well as great economy and flexibility. --PeterMerel


But you don't need MI. All that you need is a way to factor interfaces. In SmallTalk, this is trivial, since there is no typing of instances. In Java, we use single inheritence, but implement multiple interfaces. AFAIK, only in C++ and Eiffel would you use MI to solve this problem.

Generally, the reasons advanced for MI have to do with those things that you cannot do in either Java or Smalltalk - primarily use (other than through delegation) implementations of multiple superclasses. While I agree that MI is not 'evil,' I don't see that you have even addressed the issue.

--RussellGold

Russell's quite right here that there are other ways to do the refactoring bit, and interfaces in both java and objective-C are a good way to go, with the exception, again as he notes, of mixins. So I'm not saying the other ways of doing refactoring are bad, but since I like mixins I'll give them a little support as well:

Mixins are places where MI can cause maintenance troubles, however. If you have an abstract class that relies for its implementation on functionality in another abstract class that it obtains by way of a mixin, then you've really hidden a dependance between the two abstracts, and that's bad. In my experience, however, it's extremely rare, perhaps because I use this CycleAbstraction? technique, which naturally constrains the responsibilities of the abstracts. --PeterMerel


MI is useful for combining orthogonal abstractions (to quote BertrandMeyer). What else is there to say? -- DaveHarris

I agree. When you've said, "to quote BertrandMeyer," you've said quite enough. :-) ---GlennVanderburg

OK, I'll say a bit more. Firstly, most of the arguments against multiple inheritance are also arguments against single inheritance. Ditto arguments for. Let's recap the pro arguments. What is inheritance for?

The last pair of these are only issues with static type-checking, and the first can be done quite nicely with delegation idioms (especially based around #doesNotUnderstand:), so we don't much need MI in Smalltalk. Java covers the last pair with interfaces but loses out sorely on the first. It has nothing "multiple" there. The other languages need MI because they don't have decent delegation and they do have static types. -- DaveHarris

''Actually, I've said much the same thing only I phrased it as "If you're going to let people descend interface from any number of classes but implementation from only a set number, why is that set number 'one' and not some other value?" -- KatieLucas

Hmmm ... I think perhaps you should have let things be. In spite of my dig at Bertrand, the quote above does seem sensible to me. Your addition doesn't resonate, however, because I would never have supplied any of the three answers you give for the purpose of inheritance. They may or may not be nice benefits of inheritance, but they aren't what inheritance is for. ---GlennVanderburg

I think we're using different words for the same thing. At least, I read your long piece below without learning what inheritance is for other than the 3 things I list. (Incidently, I see polymorphism as all but orthogonal to inheritance.) -- DaveHarris


It seems to me that using much Multiple Inheritance might be taken as evidence of the presence in one's design of more complexity than one might actually need. It might not be evil, but it is one of the Signs of the Apocalypse. --RonJeffries

It certainly needs to be tempered by OnceAndOnlyOnce ... but how does it otherwise add complexity? --PeterMerel


The following is a personal statement --- not intended to be persuasive of others --- of why I'm glad Java does not support multiple inheritance.

Although I certainly agree that MI is not evil, I do think it is relatively unimportant and rarely useful, and I'm happy to be working in a language (Java) that does not support it. In part, I've come to believe that inheritance is given far too much importance by most OO languages, designs, developers, and pundits.

My thoughts about inheritance have been evolving since I learned Java. The revelations I've had may not seem like much to a Smalltalk programmer, but they represent a complete shift in my thinking.

Nearly every introduction to OO concepts I've ever read or seen has dealt with inheritance very early, and then moved on to a cursory discussion of "polymorphism" as a sort of nice side-effect of inheritance. Partly as a result of this (and partly because of the underlying misunderstanding) the word "inheritance" is usually used to refer to some combination of behavior inheritance and subtyping.

Java is the first language I have used that mostly separates those two concepts. Extending or implementing an interface represents a subtyping relationship, whereas extending a class represents the more traditional combination of subtyping and behavior inheritance. The process of using and designing with interfaces has brought subtyping out of the shadows and into the foreground of my thinking.

I've begun teaching the concepts of inheritance and polymorphism the other way 'round: polymorphism and interfaces come first, as the fundamental issue, and behavior inheritance comes later, as a nice facility (more, but not much more, than a convenience) when two subtypes share significant portions of their behavior or implementation. It seems to work well. Many of the common inappropriate uses of inheritance never occur to programmers who learn it this way. I've found that the method also helps in the explanation of abstract classes and when to use them.

Consider a language that makes the separation even more clear: subtyping uses one mechanism, and behavior inheritance can be specified without also producing a subtype relationship. (I'm speaking hypothetically, but I won't be surprised to learn that such a language actually exists.) Behavior inheritance becomes a kind of specialized composition or delegation facility. Some of the traditional objections and implementation complexities of MI disappear.

My conclusion, then, is that the strongly-typed OO community may have been going down a side path for most of its history, conflating two concepts that are actually separate. Perhaps, once we've corrected that misdirection, the time will come for MI to move back to center stage. For now, though, I'm pleased that Java avoids MI, if only because having to do without it has helped me to understand the fundamental issues more clearly. (From talking to colleagues, I believe it is having a similar effect on others, too.) ---GlennVanderburg

You'd might want to look at SatherLanguage, at http://www.icsi.berkeley.edu/~sather/.


Very well-reasoned and very well put, Glenn. And it has been a bad week for those dimensions.

Of course I'm contaminated now from having worked, for the past N years, in a language that is sublimely ignorant of "type". In Smalltalk, inheritance is about as close as you can get to being a programmer's hack to save code. Which is what it's for, after all.

To me, the whole subtype notion is unnecessary and (it seems to me) doesn't work anyway. When you view inheritance as a technique for implementing OnceAndOnlyOnce, it makes eminent sense to combine things in any way whatsoever, types be damned.

Now please go over and work on that stuff about classes being cyclic dependencies. ;-> --RonJeffries

Agreed that Glenn has put this very well. I'm content that, for almost all purposes, MI as behaviour inheritance isn't necessary. But not having it has consequences; I suspect we can trace the painful complexity of both JFC and MFC class libraries to not being able to do mixins. Among other things. --PeterMerel

Slightly off topic: Glenn has indeed put it very nicely (perhaps we should find a shorthand for that phrase) (trust me, that would not pay off over time. ---GV) but some compilers are not "sublimely ignorant" of types. In fact, they force you to think about inheritance as subtyping. Thus, generic delegation alla #doesNotUnderstand: does not work. With MI, you could do some mix-in stuff. But then, you don't have MI in Java. Which leads me to my question: Has anybody come up with something else like mixins to reduce code duplication in Java? An implementation pattern maybe? PeterMerel's comment right above seems to suggest no. -HaskoHeinecke


Java actually does support multiple inheritance. It just limits that support to interfaces and doesn't provide it for inheritance of implementation. It's a way of allowing polymorphism strictly by adhering to a shared interface "contract" without having to share any implementation of structure or behavior. It's still MI, it's just for subtyping instead of subclassing. The problem of name-clashes and redefinition still exists.

Note that there are really two facets of inheriting or sharing "implementation": one is inheriting structure or state (data) and the other is inheriting behavior (method implementation). There are some languages that provide inheritance of behavior but not of structure (Perl and Python spring to mind). This is kind of interesting because it forces superclass and subclass to communicate only via methods and you are forced to pay more attention to the interface between superclass and subclass (which more people need to do IMHO).

Ralph Johnson once said that C++ programmers suffer from the disease of thinking that inheritance causes polymorphism :-). Like Java, the Sather programming language (an interesting variant of Eiffel that preceded Java) explicitly separates subtyping from subclassing. In fact I believe it even does what Glenn mused about above: allowing subclasses to share more than just interface but without forcing a subtype relationship.

Inheritance isn't really a "pure" concept in OOPL theory. But Polymorphism is. The orthogonal concept is that of "Delegation." Inheritance of behavior is nothing more than a particular combination of Polymorphism and Delegation. Having separate support for both polymorphism and delegation and being able to combine them is actually much more powerful (meaning you can do more with it; including get yourself into more trouble ;-) --BradAppleton

I enjoy working in a typeful language, and find that it helps my thinking (and I have worked some in Smalltalk and some other typeless ones, so I do know a little bit, at least, about the other side of the fence). One of the things I'm excited about is the idea that, with proper separation of the concepts, inheritance can still be "a technique for implementing OnceAndOnlyOnce" without interfering with proper subtype relationships.

One thing to note is that if your design is expressed in terms of interfaces rather than classes, you do not need MI to do mixins in Java; delegation works just fine. The fact that delegation is manual and tedious can be ameliorated by tools (or possibly by the eventual addition of AutomaticDelegationForJava). Not that MI wouldn't be nice for doing mixins, but it's not necessary for the best Java designs I've seen.

Unfortunately, Java developers (me included) are still learning how to use interfaces well, how to express designs and relationships in terms of pure interfaces rather than these odd mixes of interface and behavior that we call classes. Not having MI is forcing us to learn. I think over the long run this will be useful. ---GlennVanderburg

Do you agree that it's led to unnecessary complexity in the libraries?

Yeah, but I don't think the complexity is necessarily the result of not having MI; I think the libraries would have been simpler if interfaces had been put to better use. I *think* that; I don't know that I can really back it up yet.


Note: C++ is a language in which inheritance need not produce a subtyping relationship. They call that kind "private" or "protected" inheritance. C++ offers a superset of Java in this area. -- DaveHarris

Um ... yeah, I knew that. :-) I really did, and just forgot. But I see that (my forgetting, that is) as part of the problem, which I'll get to in a moment.

In a way, C++ offers the complete separation in the other way, because you can introduce a subtype relationship without inheriting behavior, by using pure abstract classes.

So C++ technically matches what I speculated about above. But while the concepts of inheritance and polymorphism can be separarated in a C++ program, they are not conceptually separated in the language. And that conceptual separation is what I think we need. ---GlennVanderburg

C++ has much more polymorphism that you think (or remember) it has, thanks to overloading and templates. And polymorphism in STL is quite separated from the inheritance concept. --NikitaBelenki


And no one ever uses the same interfaces or protocols in Smalltalk between two or more classes that share the same method-names but don't share implementation?

Of course they do. I think that's called polymorphism. How do different methods of the same name lead us to want multiple inheritance? Is the idea to build an abstract class for the interface, so they can all inherit #implementedBySubclass?


One problem with multiple inheritance of interface only, as found in Java, is that an interface contract cannot usually be specified completely without using code. For example Stack.pop() needs the precondition that the stack be not empty, and likewise for the post-condition of Stack.push(). A Stack interface ought to be able to carry the code to enforce the contract.

This doesn't necessarily mean we need full MI, but it does mean we need more than Java provides.


When I worked in C++, I did use multiple inheritance, for the "orthogonal abstractions" reasons cited above. So when I first came to Java, I felt stifled. But then I learned that you could get the same results by composition (~= delegation), and since then I've felt it made me a better OO developer. -- TomRossen
Back in March of 2001, an exchange on MultipleInheritanceInSmalltalk supported the argument that MultipleInheritanceIsNotEvil. Here are two examples culled from that page:

These are two cases where not only can we correctly say MultipleInheritanceIsNotEvil, but in my opinion these two classes are cases where MultipleInheritance really is the best and most supportable solution. Yes, it can be simulated through delegation or protocol-copying. But the basic concept is still MultipleInheritance.

I would think that anyone who has endured the pain and suffering of implementing and supporting either class in either Smalltalk or Java (don't forget about the differences between the various flavors of numbers and the "zoo" of iterator/collection classes) would be well aware of the compelling advantage offered by MultipleInheritance in these two cases.


Personally, I use MI a LOT. I use it for adding a lot of genericity to things without needing to add code to every use of it. Suddenly I can have, for example, a template to descend from that will keep all instances of a class in a suitable dictionary so they can be found, without needing to do anything but descend from it. Without MI, any classes that use that couldn't have any other parents of any size. I don't find getting loops in the graph a problem, partly because of the way I code - I extract behaviours into very small precise classes, and layer them.

Whenever I go near SI languages, I find myself spending a lot of time writing a lot of code over and over again in order to get polymorphic behaviour to inherit so I can re-use stuff, which seems like a waste of time to me...

And you end up with arbitrary decisions about where to slice the inheritance. Is a PoliceCar? mostly a Car (has four wheels, engine, drives on roads) or an EmergencyVehicle? (has a radio, blue lights and a siren). Now the correct answer in an SI environment is that NEITHER is more important, and so people should interface-inherit from both and re-write both behaviours. In the real world I find that actually people pick one. Deem it "more important" and implementation-inherit that. Meanwhile, a partition away, someone else is making a different importance decision and by the wall at the end of the office buried in nine tons of other paper is a "policy" on how to pick them written by a committee...

--KatieLucas


Using public inheritance rather than delegation, as a tool for reusing implementation requires fewer lines of code in C++. This encourages the developer to use inheritance even when they have no intention of refining the type they are inheriting from.

I think that multiple inheritance of implementation tends to cause a very confusing model of the system - increasing maintenance costs. On the other hand, re-use by delegation in C++ tends to involve repeated descriptions of the same elements - increasing maintenance costs by violating DontRepeatYourself.

I tend towards using delegation rather than inheritance because I tend to work on systems that will be maintained by low-skill developers who will not be dedicated to a single project and thus need to understand the model very swiftly.

--DuncanForsyth


I have issues with inheritance in general for exactly the slicing problems raised above. Instead of the arbitary slicing allowed by multiple inheritance I would lean towards removing as much inheritance as possible. Afterall, is it important to know that every police car has four wheels or is it sufficient to say that a police car is a car and cars usually have four wheels. For my mind inheritance seems to incorporate a lot of redundant information. --RichardCordova

This sounds a little extreme. Instead of removing as much inheritance as possible, I would focus on code clarity and add or remove inheritance to help understanding of the code.


AnswerMe Maybe I'm missing something, but isn't AspectOrientedProgramming just another, cleaner, way to do something equivalent to MultipleInheritance?

Can you explain why you think it might be?


CategoryLanguageFeature, CategoryPolymorphism
EditText of this page (last edited January 13, 2004)
FindPage by browsing or searching

This page mirrored in WikiPagesAboutRefactoring as of July 17, 2004