Skip to content
Part IA Michaelmas Term

Multiple Inheritance and the Diamond Problem

What Is Multiple Inheritance?

Multiple inheritance means a class inherits directly from more than one parent class. It seems natural: a FlyingCar is both a Vehicle and an Aircraft, so perhaps it should inherit from both. In the abstract, multiple inheritance lets a class combine capabilities from multiple independent sources.

The diamond problem — multiple inheritance ambiguity

The Diamond Problem

The diamond problem (also called the “deadly diamond of death”) is the central difficulty with multiple class inheritance. It arises when:

  • Class A defines a method m().
  • Classes B and C both extend A and both override m() differently.
  • Class D extends both B and C.
       A
      / \
     B   C
      \ /
       D

The diamond has two distinct problems:

State Ambiguity

If A has a field int x, does D inherit one copy of x or two? If B and C each have their own copy of A’s state (two copies of x), which one does D see? If there is only one copy, how do B and C’s methods — which were written expecting their own copy — behave correctly?

Behaviour Ambiguity

Both B and C override A.m(). When code calls d.m() (where d is a D instance), which implementation runs? B’s? C’s? Both? Neither? There is no universally “correct” answer — different languages make different choices, and all have edge cases.

Why This Is Hard

The diamond problem is not merely an implementation challenge — it is a semantic one. The fundamental issue is that B and C were each written against the contract of A, possibly with conflicting assumptions about how m() should work. Combining their implementations in D forces a resolution that may violate one or both of those assumptions.

How Different Languages Handle It

LanguageApproach
C++Allows multiple class inheritance. “Virtual inheritance” (the virtual keyword on the base class) ensures a single shared copy of A’s state. Without virtual, each path contributes a separate A subobject. The programmer must explicitly resolve method ambiguity.
PythonAllows multiple class inheritance. Uses the C3 linearisation algorithm (Method Resolution Order, MRO) to produce a deterministic, monotonic ordering of base classes. When D extends B, C, the MRO is [D, B, C, A], and B.m() wins if both B and C define m().
JavaBans multiple class inheritance entirely. A class may extend exactly one superclass. Multiple inheritance of type (interfaces) is allowed, and since Java 8, multiple inheritance of behaviour (default methods) is also allowed, but with explicit compile-time resolution rules.
ScalaAllows mixin composition via traits. Traits can have state and concrete methods. Conflicts are resolved by linearisation similar to Python’s MRO.
EiffelAllows multiple inheritance with explicit renaming and redefinition — the subclass must specify how to resolve each inherited feature.

Why State Is the Hard Part

The truly difficult problem is state (fields), not behaviour (methods). For methods, there is always a resolution rule (even if it is “the programmer must specify”). But when two parent classes each carry their own copy of a grandparent’s state, the meaning of “initialising that state” and “mutating that state” becomes incoherent.

Consider: B’s constructor sets A.x = 5, C’s constructor sets A.x = 10. If D shares a single A copy, what is the value of x after construction? It depends on constructor execution order, which is fragile and non-obvious.

Java’s designers observed that the state-diamond problem had no clean resolution and chose to forbid the scenario entirely by restricting class inheritance to a single parent.

Multiple Inheritance of Type (Interfaces)

The diamond problem is much simpler for pure interfaces — interfaces that declare only abstract methods (no state, no default implementations). Consider:

interface A { void m(); }
interface B extends A { }
interface C extends A { }
class D implements B, C {
    @Override
    public void m() { System.out.println("D.m"); }
}

There is no ambiguity: D must implement m(), and does so once. There is no state to duplicate, no competing implementation to choose between. Both B and C inherit the abstract declaration of m() from A; D provides the single concrete implementation.

This is why Java has always allowed multiple interface inheritance — it creates no semantic problem. The challenge re-emerges only with default methods (Java 8+), which introduce behaviour (though still no state) to interfaces.

The Lesson

The diamond problem teaches a deeper design lesson: inheritance couples a class to its superclass’s implementation. Multiple inheritance multiplies that coupling. The problem is not just about “which method wins” — it is about the fragility that arises when a class depends on the internal structure of multiple independent class hierarchies. Java’s single-inheritance rule is a deliberate trade-off: less expressiveness in exchange for simpler semantics and less coupling.