Low-Level Design Tutorial 0/42 lessons ~6 min read Lesson 13

    Liskov Substitution Principle

    The Liskov Substitution Principle (LSP) requires subtypes to behave as expected wherever the base type appears.

    Course progress0%
    Focus
    9 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    The Liskov Substitution Principle (LSP) requires subtypes to behave as expected wherever the base type appears. If Square extends Rectangle but setWidth breaks setHeight invariants, callers of Rectangle break — classic LSP failure.

    Interviewers use LSP to judge inheritance choices. Subclasses must not strengthen preconditions, weaken postconditions, or throw unexpected exceptions.

    When LSP is hard to satisfy, switch to composition or a shared interface without inheritance.

    Understanding the topic

    Key concepts

    • Subtypes must honor base type contracts.
    • No surprising exceptions or no-ops in overrides.
    • Behavioral subtyping, not just signature matching.
    • Preconditions cannot be strengthened in subclass.
    • Postconditions cannot be weakened in subclass.
    • Invariants of superclass preserved in subclass.

    Step-by-step explanation

    1. Define explicit contract for base type methods.
    2. Write substitution test: would client code still work with subtype?
    3. Check overrides for stricter validation or different exceptions.
    4. Prefer interface segregation over forced inheritance.
    5. Use composition when behavior diverges significantly.
    6. Document allowed overrides in abstract base.

    Informative example

    Bird interface avoids Penguin extends FlyingBird LSP trap:

    java
    public interface Bird {
    String name();
    }
    public interface Flyer extends Bird {
    void fly();
    }
    public final class Sparrow implements Flyer {
    private final String name;
    public Sparrow(String name) { this.name = name; }
    @Override public String name() { return name; }
    @Override public void fly() { /* ... */ }
    }
    public final class Penguin implements Bird {
    private final String name;
    public Penguin(String name) { this.name = name; }
    @Override public String name() { return name; }
    }

    Penguin is Bird but not Flyer — no fake fly() that throws UnsupportedOperationException.

    Real-world use

    Real-world applications

    • Validating inheritance in shape, account, and stream hierarchies.
    • API design for extensible frameworks.
    • Interview traps involving square-rectangle or read-only collections.

    Best practices

    • Design interfaces around what all implementers truly share.
    • Throw UnsupportedOperationException sparingly — signals LSP smell.
    • Use factory to return most specific usable type.
    • Test subclass with base type contract tests.
    • Favor composition when behavior subsets differ.

    Common mistakes

    • Empty override or silent no-op changing meaning.
    • Subclass throws where parent succeeded.
    • Narrowing input types in override (invalid in Java anyway).
    • Returning null where parent never did.

    Advanced interview questions

    Q1BeginnerWhat is LSP?
    Objects of a superclass should be replaceable with subclass instances without breaking correctness.
    Q2BeginnerFamous LSP violation?
    Square extending Rectangle where width/height setters break rectangle assumptions.
    Q3IntermediateHow does LSP relate to inheritance?
    Inheritance is only safe when LSP holds for all public methods.
    Q4IntermediateIs throwing in override always LSP violation?
    If base contract allows failure, documented — OK. If surprise vs base — violation.
    Q5AdvancedFix read-only list extending full List interface.
    Separate ReadOnlyList interface or unmodifiable wrapper instead of subclass throwing on mutators.

    Summary

    Subtypes must honor superclass behavioral contracts. Avoid inheritance when behavior diverges. Interface segregation prevents forced unsupported methods. Test substitution, not just compile-time types. Composition alternative when LSP fails.

    Ready to mark this lesson complete?Track your journey across the entire course.