What is the difference between composition and inheritance? Why is composition often preferred?
Quick Answer
Inheritance models an 'is-a' relationship by extending a class, tightly coupling subclass to superclass implementation. Composition models a 'has-a' relationship by holding a reference to another object and delegating to it. Composition is often preferred because it avoids fragile base class problems, allows swapping implementations at runtime, and doesn't leak superclass internals across a rigid hierarchy.
Detailed Answer
Inheritance (extends) creates an "is-a" relationship: a Car is a Vehicle. The subclass automatically gets (and is coupled to) the superclass's implementation, including any future changes to it — this is the classic fragile base class problem: a seemingly safe change to a superclass can silently break subclasses.
Composition creates a "has-a" relationship: a Car has an Engine. Instead of inheriting behavior, the object holds a reference to another object and delegates to it:
// Inheritance (tightly coupled, fixed at compile time)
class ElectricCar extends Car { }
// Composition (flexible, swappable at runtime)
class Car {
private final Engine engine; // interface type
Car(Engine engine) { this.engine = engine; }
void start() { engine.start(); }
}
class ElectricEngine implements Engine { public void start() { /* ... */ } }
Why composition is often favored ("favor composition over inheritance"):
- Looser coupling — the containing class depends only on an interface, not a concrete superclass's internals.
- Runtime flexibility — you can swap the
Engineimplementation via constructor injection; inheritance locks the relationship at compile time. - Avoids deep, fragile hierarchies — no diamond-shaped confusion, no accidental overrides breaking behavior.
- Respects encapsulation — a class doesn't expose (or inherit) implementation details it doesn't need.
Inheritance still earns its place for genuine "is-a" hierarchies with strong behavioral substitutability (Liskov Substitution Principle) — e.g., Circle/Rectangle extending Shape — but composition is usually the safer default for code reuse.