Composition vs Inheritance
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
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());
}
}import java.util.ArrayList;
public class CounterBad {
static class TicketQueue extends ArrayList<String> {
void join(String name) { add(name); }
String next() { return remove(0); }
}
public static void main(String[] args) {
TicketQueue queue = new TicketQueue();
queue.join("Navika");
queue.join("Srihitha");
queue.add(0, "Advitha"); // jumps the queue: allowed!
queue.clear(); // empties everyone: allowed!
System.out.println("Waiting: " + queue.size());
}
}Serving Navika
Still waiting: 2How it works
- In the composition version,
TicketQueuehas a private list. Outside code can onlyjoin, take thenextperson, or ask thesize; the queue's rules can't be broken. - In the inheritance version,
TicketQueueis anArrayList, so it inherits around 40 public methods.add(0, ...)lets someone jump the queue;clear()empties it. The class can't stop either. - 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
| Problem | What happens |
|---|---|
| Everything is inherited | The child exposes all the parent's public methods, wanted or not |
| Fragile base class | A change inside the parent (a new method, a different internal call) can silently break children |
| Fixed at compile time | An object can't change its parent at runtime; a field can be swapped |
| Single parent only | Using 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:
- "Child is a Parent" makes sense to a non-programmer.
- Every public method of the parent makes sense on the child.
- The child can be used anywhere the parent is expected without surprises (the Liskov substitution principle, see "SOLID Principles in Java").
- 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
- Easy: Decide inheritance or composition:
CarandEngine;CircleandShape;LoggerandFile;ManagerandEmployee. - Harder: Write a
Stackclass by composition withpush,pop,peekandisEmpty, holding a privateArrayList<Integer>.
Key Takeaways
Was this tutorial helpful?