State the Liskov Substitution Principle (LSP). Mathematically a square is a rectangle — so why does making Square a subclass of a mutable Rectangle class violate LSP? How would you fix the design?
Show hints
Write a client function that sets width and height and checks the area.
Show the explanation
Model answer.
The principle: objects of a subtype must be usable wherever objects of the base type are expected, without breaking the correctness of the program. A subclass may extend behaviour but must honour the base class's contract: it must not strengthen preconditions, weaken postconditions or break invariants that clients rely on.
The broken design
class Rectangle {
protected int w, h;
void setWidth(int w) { this.w = w; }
void setHeight(int h) { this.h = h; }
int area() { return w * h; }
}
class Square extends Rectangle {
void setWidth(int s) { w = s; h = s; }
void setHeight(int s) { w = s; h = s; }
}A client written against Rectangle reasonably expects that setting the width does not change the height:
void resize(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert r.area() == 20;
}Pass a Square and the area is 16, because setHeight(4) also changed the width. The subclass silently changed the behaviour the base class promised, so it is not substitutable — an LSP violation. The geometry fact "every square is a rectangle" holds for immutable shapes, but the behaviour of a mutable Rectangle (independent width and height) is not something a square can provide.
Fixes
- Make shapes immutable:
new Rectangle(5, 4)andnew Square(4). Without setters, there is no contract a square can break, andSquarecan safely be a kind of rectangle (or simply a factory for one). - Use a common abstraction instead of inheritance between them: both implement a
Shapeinterface witharea(); neither extends the other. - Avoid "fixing" it with
instanceofchecks in clients — that is exactly the coupling LSP is meant to prevent.
LSP warning signs: a subclass throws UnsupportedOperationException for an inherited operation, overrides that silently do nothing, or clients checking the concrete type first.
Follow-up: "Where does Java's standard library bend this rule?" Collections.unmodifiableList returns a List whose add throws UnsupportedOperationException — the List contract marks such operations as optional, which is a documented compromise.
Original UniCircle question. Last reviewed 28 Oct 2026.
