advanced.java
← All concepts
Class design · ch. 3, p. 25

Inheritance vs composition

Inheritance couples a subclass to its parent's implementation details, so a parent change can silently break it. Composition delegates to a held instance and depends only on the published contract.

The classic demonstration:

class CountingSet<E> extends HashSet<E> {
    private int added = 0;

    @Override public boolean add(E e) { added++; return super.add(e); }

    @Override public boolean addAll(Collection<? extends E> c) {
        added += c.size();
        return super.addAll(c);          // which itself calls add()
    }
}

HashSet.addAll happens to be implemented in terms of add, so every element is counted twice. Nothing in the documented contract said so, and a future release could change it either way.

The composed version cannot break:

class CountingSet<E> {
    private final Set<E> delegate = new HashSet<>();
    private int added = 0;

    public boolean add(E e) { added++; return delegate.add(e); }
}

When inheritance is right

Only for a true “is-a” relationship where the supertype was designed for extension — meaning it documents its self-use patterns, or it is abstract with clearly designated hook methods. Anything else should be final.

2 questions on this concept Drill it