Question bank — SOLID — Liskov Substitution Principle
Before you start, hold these three words in mind — they carry the whole principle:
- Precondition — what a method demands of its caller before it will work.
- Postcondition — what a method promises to deliver when it finishes.
- Invariant — a truth about the object that stays true before and after every method.
Picture the two words that trip everyone up: covariant vs contravariant
"Variance" just answers one question: when I go from base to subtype, is a set allowed to grow or must it shrink? Look at the figure — it shows the two allowed directions and why they never surprise a caller who only knows the base.

See the Rectangle–Square break happen at runtime
The classic trap: math says a square is a rectangle, but behaviourally a mutable Square cannot honour setWidth and setHeight independently. The figure traces exactly what a Rectangle-expecting client observes, step by step.

The Bird hierarchy — see the fix, don't just read it
Prose says "split the hierarchy". The class diagram makes the subtype relationships obvious: Penguin must not sit under whatever declares fly().
True or false — justify
A square is a rectangle, so Square extends Rectangle always satisfies LSP.
setWidth's independent-dimension invariant that a Rectangle caller relies on.LSP is really just a restatement of "inheritance should model IS-A relationships".
A subclass that accepts more input types than its base still obeys LSP.
A subclass that guarantees more than the base (a stronger postcondition) obeys LSP.
If the method signatures match exactly, LSP is automatically satisfied.
Throwing UnsupportedOperationException in an override is fine because it documents a limitation.
Making all fields final (immutable) in both Rectangle and Square can restore substitutability.
setWidth/setHeight mutators, there is no contract for a Square to violate — both only promise area(), which each honours fully.LSP violations are always caught by the compiler.
Overriding a method to return a narrower (more specific) return type breaks LSP.
Overriding a method to accept a narrower parameter type is safe because "stricter is safer".
A static method can be "overridden" to change behaviour under LSP just like an instance method.
Spot the error
class Penguin extends Bird { void fly(){ throw new UnsupportedOperationException(); } } — what LSP rule is broken?
Bird's contract; any code calling bird.fly() is correct for a Bird but crashes for a Penguin.Square.setHeight(4) also sets width to 4 — which part of the contract does this violate?
setHeight (it no longer guarantees width is unchanged).An override void save(File f) { if (f.isLocal()) super.save(f); else throw ...; } — spot the flaw.
A base read() promises "returns non-null string"; the override sometimes returns null. What's wrong?
NullPointerException, breaking correctness.FlyingBird extends Bird with Penguin extends Bird and Eagle extends FlyingBird — is this a valid fix?
FlyingBird, so no caller of FlyingBird.fly() can ever receive a Penguin.A subclass override adds a synchronized lock and can now deadlock under contention the base never could — LSP problem?
A subclass has a field int size = 3 while the base has int size = 0; a method reads this.size. Does hiding this field cause an LSP trap?
size (0) while the object "really" holds 3; the two views disagree and can surprise the caller. Prefer private fields with accessor methods.Why questions
Why are preconditions contravariant (may only widen)?
Why are postconditions covariant (may only narrow)?
Why does an LSP violation destroy the value of Polymorphism?
if (it's really a Square) ... type checks, which defeats the whole point of writing code once against a base type and trusting all subtypes.Why is "fix the hierarchy" preferred over "throw NotImplemented" for a capability a subtype lacks?
Why does LSP connect to Design by Contract?
Why does favouring composition (a shared Shape interface) often dodge the Rectangle/Square trap?
area()), so there's no over-broad contract like setWidth for a subtype to break.Why is LSP considered the "glue" that lets SOLID — Open Closed Principle work?
Why might a base method be declared final specifically to protect LSP?
final method cannot be overridden, so no subtype can weaken its postcondition or break its invariant — the base guarantees that piece of the contract stays intact for every subtype.Edge cases
Does LSP apply when a subclass adds new methods the base doesn't have?
A base method promises to complete in O(n); the override runs in O(n²). LSP violation?
An abstract base method has no implementation; can a subclass override it however it likes?
Is it an LSP violation for a subclass to make an operation faster or return extra fields?
A subclass constructor requires an extra non-null argument the base didn't. Does LSP care?
Does LSP say anything about a subtype throwing a subclass of an exception the base already declares?
Does overriding a final method to change behaviour risk an LSP violation?
final forbids the override entirely; the corner case is the reverse, where marking a method final removes the very possibility of a subtype breaking that contract.Can a private method create an LSP problem when a subclass declares one with the same name?
private methods aren't polymorphic, so each class calls its own; there's no runtime substitution, hence LSP (about substitutable dispatch) is not engaged for them.Recall One-line self test before you close this page
If I hand a subtype where a base type was expected, can the caller — who knows only the base contract — ever be surprised? ::: If yes, LSP is violated; the fix is to weaken preconditions, strengthen postconditions, preserve invariants, or restructure the hierarchy so the mismatch is impossible.