CMD Guide
HomeOO & Low-Level DesignSOLID Principles

Using Composition to Follow Liskov Substitution Principle LSP

Composition over inheritance — actually using composition this time

When inheritance forces a subtype to break its parent's contract (the LSP problem), the durable fix is usually composition: instead of being the base class, the type has one and exposes only the operations it can honour. The earlier page was titled "composition" but its example still used inheritance — so here is the real thing.

The classic: Stack should not extend ArrayList

// WRONG: inheritance leaks operations that violate the stack's invariant
class Stack<E> extends ArrayList<E> { }
// a caller can now do stack.add(0, x) or stack.remove(3) — LIFO invariant destroyed,
// yet Stack "is-a" ArrayList so all of it is exposed.
// RIGHT: composition — Stack HAS-A list, exposes ONLY push/pop/peek
class Stack<E> {
    private final java.util.List<E> items = new java.util.ArrayList<>();   // delegate
    public void push(E e) { items.add(e); }
    public E pop() {
        if (items.isEmpty()) throw new java.util.NoSuchElementException();
        return items.remove(items.size() - 1);
    }
    public E peek() { return items.get(items.size() - 1); }
    public boolean isEmpty() { return items.isEmpty(); }
}

Now there is no Stack method that can violate LIFO, because the ArrayList is hidden behind a private field and only the safe operations are forwarded. Substitutability is no longer a question — Stack never claims to be an ArrayList.

Why composition dodges the LSP trap

Rule of thumb: use inheritance only when the subtype is genuinely substitutable for the base in every context (true "is-a" behaviourally). Otherwise compose and delegate.

The formal test to say out loud: any property provable about instances of the base type must still hold when a subtype instance is used through a base-type reference. Inheritance makes that a promise about every inherited method; composition lets you make it about only the handful you chose to expose.

Interview drill: Java Properties extends Hashtable

A historical JDK design failure (called out in Effective Java): java.util.Properties extends Hashtable<Object,Object>. Callers can therefore do:

Properties p = new Properties();
p.put(42, new Object());   // legal via Hashtable API — keys/values need not be Strings
String v = p.getProperty("x"); // Properties API assumes String keys/values

The inherited put/get methods break the Properties contract (string-only property bag). That is an LSP violation: a Properties object is not a safe Hashtable for every Hashtable client, and Hashtable methods are not safe for every Properties use.

What would composition look like?

class Properties {
    private final Map<String, String> map = new HashMap<>(); // has-a, not is-a
    public String getProperty(String key) { return map.get(key); }
    public void setProperty(String key, String value) {
        map.put(Objects.requireNonNull(key), Objects.requireNonNull(value));
    }
    // optional: load/store, defaults — still no raw put(Object,Object)
}

Now nothing can insert a non-String key through the public API. Reuse of map storage is selective; the type never claims to be a general Hashtable.

Self-check: Name one other JDK or library type that inherited a collection and leaked mutators. How would you redesign it with a private final collection field?

Takeaways


Re-authored for correctness for this guide (the prior version described "composition" but coded inheritance). Per "Effective Java" (favor composition over inheritance) & SOLID/LSP. See also: Break the Hierarchy for LSP, Decorator.

🤖 Don't fully get this? Learn it with Claude

Stuck on Using Composition to Follow Liskov Substitution Principle LSP? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Using Composition to Follow Liskov Substitution Principle LSP** (OO & Low-Level Design) and want to truly understand it. Explain Using Composition to Follow Liskov Substitution Principle LSP from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Using Composition to Follow Liskov Substitution Principle LSP** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Using Composition to Follow Liskov Substitution Principle LSP** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Using Composition to Follow Liskov Substitution Principle LSP** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes