पाठ 9 / 25
When Inheritance Goes Wrong
Recognise fragile base classes, deep hierarchies, the diamond problem and LSP violations.
Powerful, and easy to misuse
Inheritance couples a subclass tightly to its parent's implementation. The fragile base class problem: a change to a superclass, even an internal one, can break subclasses that relied on how it worked. A classic example is extending HashSet to count additions by overriding both add and addAll, then double-counting because addAll calls add internally. Deep hierarchies (five or six levels) become hard to understand, because behaviour is scattered across many classes. The diamond problem arises with multiple inheritance: if B and C both inherit from A and override a method, and D inherits from both, which version does D get? C++ addresses it with virtual inheritance, Python with a defined method resolution order (MRO) using C3 linearisation, and Java avoids it for classes but must resolve conflicting default methods in interfaces, where the class must override and choose explicitly. Most importantly, a subclass must not break the parent's contract, the Liskov substitution principle covered later. These problems are why designers say "favour composition over inheritance".
The diamond with Java default methods
Two interfaces provide the same default method; the class must resolve the conflict.
interface Flyer { default String move() { return "fly"; } }
interface Swimmer { default String move() { return "swim"; } }
class Duck implements Flyer, Swimmer {
@Override
public String move() { // required: otherwise compile error
return Flyer.super.move() + " and " + Swimmer.super.move();
}
}
// Python resolves with the method resolution order (MRO)
// class A: def hi(self): return "A"
// class B(A): def hi(self): return "B"
// class C(A): def hi(self): return "C"
// class D(B, C): pass
// D().hi() -> "B" (D.__mro__: D, B, C, A, object)Design for inheritance or forbid it
Joshua Bloch's advice in Effective Java: either document precisely how a class may be extended (which methods call which) or make it final. Accidental extensibility is a source of fragile subclasses.
त्वरित जाँच: How does Java handle a class that implements two interfaces with conflicting default methods?
- It picks one at random
- It is a runtime error
- The class must override the method and resolve the conflict explicitly
- The first interface listed always wins
Answer
The class must override the method and resolve the conflict explicitly — The compiler requires the class to override and may call InterfaceName.super.method().