Skip to content
UniCircle
MediumConceptualSOLID principles

Liskov Substitution Principle: the square–rectangle case

Question OOP-107

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

Java
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:

Java
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) and new Square(4). Without setters, there is no contract a square can break, and Square can safely be a kind of rectangle (or simply a factory for one).
  • Use a common abstraction instead of inheritance between them: both implement a Shape interface with area(); neither extends the other.
  • Avoid "fixing" it with instanceof checks 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.