⚡ Play a quiz
HomeLearnCS CoreOOPs Concepts › Inheritance vs composition
Lesson 2 of 3 · OOPs Concepts

Composition Over Inheritance, With Examples

OOPs Concepts67%

Inheritance is the first tool taught and the one most often misused. It couples a subclass to its parent's internals permanently, and that coupling is what makes old codebases rigid. Composition — holding an object instead of extending it — solves the same problems with far less commitment.

Read time
7 min
Track
CS Core
Sections
3
Practice
0

Where inheritance breaks

  • Fixed at compile time. A subclass cannot change what it inherits while running; a composed dependency can be swapped.
  • The fragile base class. Changing a parent method silently alters every subclass, including ones written by other teams.
  • Combinatorial explosion. Three orthogonal traits force eight subclasses to cover the combinations.
  • One parent only. In Java and C#, a class inherits from exactly one class, so the first inheritance decision spends the budget.
Inheritance forcing a class per combination
class Bird { void fly() {} void swim() {} }

class Penguin extends Bird {
    void fly() { throw new UnsupportedOperationException(); }   // a lie
}

Penguin is a bird but cannot fly, so it inherits a method it must refuse — and every caller holding a Bird can now crash. That is a Liskov violation, and the compiler will not catch it.

The composition version

Capabilities as separate contracts
interface Flies    { void fly(); }
interface Swims    { void swim(); }

class Sparrow implements Flies          { public void fly()  { /* ... */ } }
class Penguin implements Swims          { public void swim() { /* ... */ } }
class Duck    implements Flies, Swims   { public void fly()  { }  public void swim() { } }

No class carries a method it cannot honour, capabilities combine freely, and a new one is a new interface rather than a rewrite of the hierarchy.

Choosing between them
QuestionInheritanceComposition
Relationshipis-ahas-a / can-do
BoundAt compile timeAt runtime, swappable
CouplingTight, to the parent's internalsLoose, to a contract
TestingNeeds the real parentInject a fake
Good forA genuine subtype with shared behaviourReusing behaviour across unrelated types
tip

The rule to state in an interview

Use inheritance only when the subclass is genuinely substitutable for the parent everywhere. If you are reaching for it just to reuse code, use composition — has-a beats is-a whenever both would work.

INHERITANCE — one parent, every capability baked in Bird fly() · swim() Sparrow Penguin Duck inherits fly() → must throw → a caller holding a Bird crashes COMPOSITION — capabilities as separate contracts Flies Swims Sparrow Duck Penguin
Inheritance forces a class per combination and leaves Penguin with a method it has to refuse — which is what breaks any caller holding a Bird.

How this connects to SOLID

  • S — Single responsibility. One reason to change per class; large inheritance trees usually violate it first.
  • O — Open/closed. Open for extension, closed for modification — which is exactly what polymorphic dispatch buys.
  • L — Liskov substitution. A subclass must work anywhere its parent does. The penguin fails this.
  • I — Interface segregation. Many small contracts beat one large one; Flies and Swims instead of Bird.
  • D — Dependency inversion. Depend on abstractions, not concrete classes — which is composition through an interface.

Key takeaways

  • Inheritance is permanent, compile-time coupling to a parent's internals.
  • If a subclass must refuse an inherited method, the hierarchy is wrong.
  • Composition combines capabilities freely and can be swapped at runtime.
  • Prefer inheritance only for genuine, fully substitutable subtypes.

Frequently asked questions

Why is composition preferred over inheritance?

Composition couples to a contract rather than to a parent's implementation, can be changed at runtime, avoids the fragile base class problem, and lets capabilities combine without a class per combination.

When is inheritance the right choice?

When the subclass genuinely is a kind of the parent and can be substituted for it everywhere, and when there is real shared state and behaviour to inherit rather than just code you want to reuse.

What is the fragile base class problem?

Changing a base class can silently break subclasses that depended on its internal behaviour, even though the subclasses were not modified and the change looked safe in isolation.

Test yourself on OOPs Concepts

Reading is not recall. Take a timed quiz on this topic solo, or share a room code and battle friends on it.

⚡ Start the OOPs Concepts quiz

More in OOPs Concepts