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
- Inheritance is "is-a" and total — a subclass inherits every public method of the base, including ones that break its invariants. You can't subtract.
- Composition is "has-a" and selective — you forward only the methods that make sense, so you can't accidentally expose an operation that violates a contract.
- It also avoids the fragile-base-class problem: changes to
ArrayListcan't silently changeStack's behaviour, becauseStackonly depends on the small slice it calls.
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
- Composition lets a type expose only the operations it can honour — inheritance forces it to expose all of them.
- "Has-a + delegate" sidesteps LSP violations and the fragile-base-class problem.
- Reach for inheritance only on true behavioural "is-a"; otherwise prefer composition.
Properties extends Hashtableis the canon interview example of inheritance that should have been composition.- Code-review fingerprint: grep domain types for
extends ArrayList/HashMap/Hashtable/Collection— a business type inheriting a collection is almost always a leaked-mutator LSP bug waiting to happen; replace it with a private final collection field and a forwarded, invariant-safe API.
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.
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.
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.
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.
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.