SOLID Principlescritical

Liskov Substitution Principle (LSP)

Subtypes must be substitutable for their base types without altering the correctness of the program. Any code that uses a base class reference must work correctly when handed any of its subclasses.

Memory anchor

LSP = hiring a substitute teacher. The students (callers) shouldn't notice anything weird. If the sub starts teaching yoga instead of math, the substitution is broken — same title, wrong behavior.

Expected depth

LSP violations typically manifest as: (1) a subclass that throws UnsupportedOperationException for inherited methods; (2) a subclass that strengthens preconditions (requires more) or weakens postconditions (guarantees less); (3) a subclass that changes the semantic contract of overridden methods. The canonical example is Square extending Rectangle: setWidth on a Square must also change height, breaking any code that sets width and height independently on a Rectangle reference.

Deep — senior internals

Formally, LSP requires: contravariance of method parameter types (subclass can accept wider types), covariance of return types (subclass can return narrower types), no new exceptions not in the supertype contract, invariant preservation, and behavioural compatibility (same meaning, not just same signature). Most OOP languages enforce syntactic covariance and contravariance partially, but behavioural compatibility is a programmer responsibility. LSP is the principle that tells you when inheritance is wrong: if you find yourself writing special-case logic ('if x is SquareSubtype then…'), the hierarchy is broken. The fix is almost always to flatten to an interface and use composition.

🎤Interview-ready answer

LSP says any subclass instance must be usable wherever the parent type is expected, without the caller needing to know the difference. The test: if I hand your code a subclass reference wrapped in a base-type variable, does it still behave correctly? The Square-Rectangle example breaks LSP because Square's setWidth changes height, violating the Rectangle contract. The right fix is not to make Square extend Rectangle — instead, model them as separate implementations of a Shape interface.

Common trap

Interviewers often ask about the Square-Rectangle problem. The answer is: inheritance models 'is-a' in a behavioural sense, not a taxonomic one. A square is a rectangle in geometry but not in software because the mutability contracts differ. If both are immutable value objects (area(), perimeter()), the hierarchy is fine.

Related concepts