Skip to main content

Composition, Inheritance & Delegation

intermediate14 min readLesson 69 of 180

Why addAll bypasses your override, and how composition with delegation sidesteps fragile base classes.

Composition, inheritance, and delegation

Inheritance is the strongest coupling Java offers: the subclass depends on superclass internals and survives every superclass change. Prefer composition: hold a collaborator and delegate.

// inheritance-flavored
class CountingList extends ArrayList<String> {
    int count = 0;
    @Override public boolean add(String s) { count++; return super.add(s); }
    // broken: addAll() bypasses add(), remove() desyncs count...
}

// composition-flavored
final class CountingList {
    private final List<String> inner = new ArrayList<>();
    int count = 0;
    void add(String s) { count++; inner.add(s); }
    int size() { return inner.size(); }
}

The inheritance version inherits behavior it never asked for (addAll skips the override โ€” a classic bug). The composition version exposes exactly the surface it wants, and counting cannot desync.

Rule of thumb: is-a that survives every future change โ†’ inheritance. has-a, wrapping, decorating, adapting โ†’ composition. When in doubt, compose and delegate.

Now practice

Design Lab: Payments & ShapesInject a fake gateway, dispatch sealed shapes exhaustively, and build a desync-proof delegating log.3 challenges ยท ยท ~40 min