What is the difference between composition and inheritance? Why is composition often preferred?

9 minintermediatecompositioninheritancedesign

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 Engine implementation 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.