Composition vs Inheritance

Naveen AnamaneniUpdated · 5 min read

A new developer wants a Stack and notices that ArrayList already stores items, so they write class Stack extends ArrayList. Now anyone can call stack.add(0, x) and insert into the middle of the "stack", breaking its whole point. The classic advice, "favor composition over inheritance", comes from bugs exactly like this. This lesson shows when each is the right tool, and how to refactor from one to the other.

The Same Goal, Two Designs

Inheritance exposes everything; composition exposes only what you choose
Counter.java▶ Try it
import java.util.ArrayList;
import java.util.List;
 
public class Counter {
    static class TicketQueue {
        private final List<String> waiting = new ArrayList<>();
 
        void join(String name) { waiting.add(name); }
        String next() { return waiting.remove(0); }
        int size() { return waiting.size(); }
    }
 
    public static void main(String[] args) {
        TicketQueue queue = new TicketQueue();
        queue.join("Navika");
        queue.join("Srihitha");
        queue.join("Advitha");
        System.out.println("Serving " + queue.next());
        System.out.println("Still waiting: " + queue.size());
    }
}
Output
Serving Navika
Still waiting: 2

How it works

  1. In the composition version, TicketQueue has a private list. Outside code can only join, take the next person, or ask the size; the queue's rules can't be broken.
  2. In the inheritance version, TicketQueue is an ArrayList, so it inherits around 40 public methods. add(0, ...) lets someone jump the queue; clear() empties it. The class can't stop either.
  3. A ticket queue isn't a kind of list; it uses a list. That's the signal to compose.

Try it yourself: add a method boolean isEmpty() to the composition version. Which list method does it call?

Why Inheritance Can Be Fragile

ProblemWhat happens
Everything is inheritedThe child exposes all the parent's public methods, wanted or not
Fragile base classA change inside the parent (a new method, a different internal call) can silently break children
Fixed at compile timeAn object can't change its parent at runtime; a field can be swapped
Single parent onlyUsing up extends for code reuse blocks it for a real is-a relationship

Composition avoids all four: the outer class decides what to expose, depends only on the inner object's public methods, and can swap the inner object at runtime.

A Checklist

Use inheritance only if all of these are true:

  1. "Child is a Parent" makes sense to a non-programmer.
  2. Every public method of the parent makes sense on the child.
  3. The child can be used anywhere the parent is expected without surprises (the Liskov substitution principle, see "SOLID Principles in Java").
  4. The parent was designed to be extended, or you control both classes.

If any answer is no, use composition.

Common Mistakes

Extending a collection or utility class for convenience. extends ArrayList, extends HashMap, extends Thread are classic mistakes; hold one instead.

Deep hierarchies to share small bits of code. A helper object shared by composition is simpler.

Forbidding inheritance completely. Real is-a hierarchies, such as account types or shapes, are a perfect fit for it.

Interview Corner

What is the difference between composition and inheritance, and which should you prefer?

Inheritance (is-a) makes a class a subtype of another and gives it all the parent's public members. Composition (has-a) makes a class hold another object as a field and expose only what it chooses. Composition is usually preferred for reusing code, because it keeps encapsulation, avoids fragile base classes and can change at runtime; inheritance is right when the child truly is a kind of the parent and honors its full contract.

Why is extending ArrayList to build a Stack a bad idea?

A stack should only push and pop at one end, but a subclass of ArrayList inherits methods like add(index, item), remove(index) and clear(), which let callers break the stack's rules. Holding a list privately and exposing only push, pop and peek keeps the stack correct.

Practice

  1. Easy: Decide inheritance or composition: Car and Engine; Circle and Shape; Logger and File; Manager and Employee.
  2. Harder: Write a Stack class by composition with push, pop, peek and isEmpty, holding a private ArrayList<Integer>.

Quick check: Composition vs inheritance

4 questions
1What is the main risk of class TicketQueue extends ArrayList<String>?
2Composition means:
3When is inheritance the right choice?
4What is the fragile base class problem?

Key Takeaways

Was this tutorial helpful?

Lesson 119 of 482