15

Java omits multiple inheritance on the grounds that it obviates the design goal of keeping the language simple.

I wonder if Java (with its eco-system) is really "simple". Python is not complex and has multiple inheritance. So without being too subjective, my question is...

What are the typical problem patterns that benefit from a code designed to make heavy use of multiple inheritance

4
  • 8
    Java omits an awful lot of perfectly good things for very little reason. I wouldn't expect a decent justification for MI.
    – DeadMG
    Aug 14, 2011 at 12:50
  • 2
    Python's multiple inheritance is definitely here-be-dragons territory. The fact that it uses depth-first, left-to-right name resolution has significant issues for both maintainability and comprehension. While it can be useful in shallow class hierarchies, it can be a incredibly counter-intuitive in deep ones.
    – Mark Booth
    Aug 15, 2011 at 13:00
  • I think the reason that Java does not contain multiple inheritance is that the Java developers wanted their language to be easy to learn. Multiple inheritance, while incredibly powerful in some cases, is hard to grasp, and even harder to be used to good effect; it's not the thing you would want to confront a programming freshman with. I mean: How do you explain virtual inheritance to someone who's struggeling with the concept of inheritance itself? And, since multiple inheritance is also not exactly trivial on the implementators side, the Java dev's likely though that omitting it is a win-win. Jan 6, 2016 at 21:13
  • Java is nominally typed. Python isn't. This makes multiple inheritance much easier to both implement and understand in Python.
    – Jules
    Jan 7, 2016 at 13:22

7 Answers 7

11

Pros :

  1. It sometimes allow more obvious modeling of a problem than other ways to model it.
  2. If the different parrents have orthogonal purpose, it can allow some kind of compositing

Cons :

  1. If the different parents don't have orthogonal purpose, it makes the type difficult to understand.
  2. It's not easy to understand how it is implemented in a language (any language).

In C++ a good example of multiple inheritance used to composite orthogonal features is when you use CRTP to, for example, setup a component system for a game.

I've started to write an example but I think a real world example is more worth looking at. Some code of Ogre3D uses multiple inheritance in a nice and very intuitive way. For example, the Mesh class inherit from both Resources and AnimationContainer. Resources expose the interface common to all resources and AnimationContainer expose the interface specific for manipulating a set of animations. They are not related, so it's easy to think about a Mesh as being a resource that in addition can conain a set of animations. Feels natural isn't it?

You can look at other examples in this library, like the way memory allocation is managed in a fined grain way by making classes inherit from variants of a CRTP class overloading new and delete.

As said, the main problems with multiple inheritance rises from mixing related concepts. It makes the language have to set complex implementations (see the way C++ allows to play with the diamond problem...) and the user not being sure what's happening in that implementation. For example, read this article explaining how it is implemented in C++.

Removing it from the language helps avoiding people who don't know how the language is inforced to make things bad. But it forces to think in a way that, sometimes, don't feel natural, even if it's edge cases, it happen more often that you might think.

4
  • i would really appreciate if you adorned your answer with an example problem -- that will make terms like "orthogonal purpose" clearer -- but thanks
    – treecoder
    Aug 14, 2011 at 12:14
  • Ok let me try to add something.
    – Klaim
    Aug 14, 2011 at 14:19
  • Ogre3D is not any place I would look to for design inspiration- have you seen their Singleton infection?
    – DeadMG
    Aug 14, 2011 at 17:13
  • First, heir singleton isn't really a singleton, the construction and destruction is explicit. Next, Ogre is a layer over a hardware system (or the graphic driver if you prefer). That mean there should be only one unique representation for system interfaces (like Root or others). They can remove some singleton but that's not the point here. I've voluntarly avoided to point this to avoid having a troll discussion, so please, look at the examples I've pointed. Their use of Singleton might not be perfect but it's clearly useful in practice (but only for their kind of system not everything).
    – Klaim
    Aug 14, 2011 at 17:19
4

There is a concept called mixins that is used heavily in more dynamic languages. Multiple inheritance is one way in which mixins can be supported by a language. Mixins are generally used as a way for a class to accumulate different pieces of functionality. Without multiple inheritance, you have to use aggregation / delegation to get mixin type behavior with a class, which is a bit more syntax heavy.

2
  • +1 this is actually a good reason to have multiple inheritence. Mixins carry additional connotations ("this class shouldn't be used as a stand-alone")
    – ashes999
    Nov 9, 2013 at 17:49
  • Multiple inheritance is just one way to implement mixins. For example, some of Java interfaces essentially encode mixin functionality, but as we know, Java doesn't support multiple class inheritance per se.
    – pfalcon
    Jan 14, 2021 at 8:43
2

I think the choice is mainly based on issues due to the diamond problem.

Moreover, it is often possible to circumvent the use of multiple inheritance by delegation or other means.

I'm not sure of the meaning of your last question. But if it is "in which cases is multiple inheritance useful?", then in all cases where you would like to have an object A having functionalities of objects B and C, basically.

2

I won't delve much in here but you can surely understand the multiple inheritance in python via the following link http://docs.python.org/release/1.5.1p1/tut/multiple.html :

The only rule necessary to explain the semantics is the resolution rule used for class attribute references. This is depth-first, left-to-right. Thus, if an attribute is not found in DerivedClassName, it is searched in Base1, then (recursively) in the base classes of Base1, and only if it is not found there, it is searched in Base2, and so on.

...

It is clear that indiscriminate use of multiple inheritance is a maintenance nightmare, given the reliance in Python on conventions to avoid accidental name conflicts. A well-known problem with multiple inheritance is a class derived from two classes that happen to have a common base class. While it is easy enough to figure out what happens in this case (the instance will have a single copy of ``instance variables'' or data attributes used by the common base class), it is not clear that these semantics are in any way useful.

This is just a small paragraph but big enough to clear the doubts I guess.

1
  • "This is depth-first, left-to-right." - since 2.x something, Python upgraded to much more adequate "C3" algorithm for multiple inheritance resolution. (Which is of course only confirms that multiple inheritance is hard, and unless you really need it, you should avoid it.)
    – pfalcon
    Jan 14, 2021 at 8:45
1

One place where multiple inheritance would be useful is a situation where a class implements several interfaces, but you would like to have some default functionality built in to each interface. This is useful if most of the classes that implement some interface want to do something the same way, but occasionally you need to do something different. You can have each class with the same implementation, but it makes more sense to push it up into one location.

1
  • 1
    Would that require generalized multiple inheritance, or simply a means by which an interface can specify default behaviors for unimplemented methods? If interfaces could only specify default implementations for the methods they themselves implement (as opposed to those they inherit from other interfaces) such a feature would completely avoid the double-diamond issues which make multiple inheritance difficult.
    – supercat
    Dec 24, 2013 at 18:13
1

What are the typical problem patterns that benefit from a code designed to make heavy use of multiple inheritance?

This is just one example but one I find invaluable to improve safety and mitigate temptations to apply cascading changes throughout either callers or subclasses.

Where I've found multiple inheritance incredibly useful even for the most abstract, stateless interfaces is the non-virtual interface idiom (NVI) in C++.

They're not even really abstract base classes so much as interfaces that have just a little bit of implementation to them to enforce the universal aspects of their contracts, as they're not really narrowing the generality of the contract so much as better enforcing it.

Simple example (some might check that a file handle passed in is open or something like that):

// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
    // Pre: x should always be greater than or equal to zero.
    void f(int x) /*final*/
    {
        // Make sure x is actually greater than or equal to zero
        // to meet the necessary pre-conditions of this function.
        assert(x >= 0);

        // Call the overridden function in the subtype.
        f_impl(x);
    }

protected:
    // Overridden by a boatload of subtypes which implement
    // this non-virtual interface.
    virtual void f_impl(int x) = 0;
};

In this case, maybe f is called by a thousand places in the codebase, while f_impl is overridden by a hundred subclasses.

It would be difficult to do this kind of safety check in all 1000 places that call f or all 100 places that override f_impl.

By just making this entry point to the functionality nonvirtual, it gives me one central place to perform this check. And this check is not reducing the abstraction in the slightest, as it is simply asserting a precondition required to call this function. In a sense, it's arguably strengthening the contract provided by the interface, and relieving the burden of checking the x input to make sure it conforms to valid preconditions in all 100 places that override it.

It's something I wish every language had, and also wished, even in C++, that it was a little more of a native concept (ex: not requiring us to define a separate function to override).

This is extremely useful if you didn't do this assert in advance, and realized you needed it later when some random places in the codebase were encountering negative values being passed to f.

0

First: multiple copies of base class (a C++ issue) & tight coupling between base and derived classes.

Second: multiple inheritance from abstract interfaces

1
  • are you suggesting it is not useful in any context? And that everything can be designed/coded conveniently without it? Also please elaborate on the second point.
    – treecoder
    Aug 14, 2011 at 12:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.