Liskov Substitution Principle (LSP) explained
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
The Liskov Substitution Principle says that code written against a base type should remain correct when any subtype is supplied. The compiler checks method signatures; LSP checks the behavior that clients are promised.
For example, if a mutable Rectangle contract lets a caller set width and height independently, a Square subtype cannot preserve that contract because changing one side changes the other. If both are immutable Shape implementations exposing only area(), the conflict disappears because the contract is different.
A safe subtype does not strengthen preconditions, weaken required results, break invariants, or remove guaranteed operations. The design question is whether the replacement preserves client-observable behavior, not whether the subtype has a mathematically related name.
Liskov Substitution Principle (LSP) explained
Direct Answer
The Liskov Substitution Principle says code written for a base type should continue to meet its contract when given any subtype. The subtype must accept the base type's valid uses, preserve required results and invariants, and avoid removing promised capabilities. Compiling under the same type is not enough.
Code Example
// Parent class with independent width/height
class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int getArea() { return width * height; }
}
// Square enforces width == height, violating LSP
class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // Caller doesn't expect this side effect
}
@Override
public void setHeight(int height) {
this.width = height; // Breaks Rectangle contract
this.height = height;
}
}
// This method works for Rectangle but fails for Square
void testRectangle(Rectangle rect) {
rect.setWidth(5);
rect.setHeight(4);
assert rect.getArea() == 20; // Fails for Square: returns 16
}
Common Mistakes & Pitfalls
- Inheritance based on real-world taxonomy: Freshers see "Square is a Rectangle in math" and create that inheritance, ignoring that the behavioral contract (independent width/height) doesn't match.
- Thinking compilation equals correctness: If your Square subclass compiles when passed to a method expecting Rectangle, freshers assume LSP is satisfied — but runtime assertion failures or wrong calculations reveal the violation.
- Overriding with narrower behavior: Creating a ReadOnlyUserRepository that extends UserRepository but throws exceptions on save() — callers expecting UserRepository get runtime crashes. The compiler allows it, but it violates the substitution principle.
Check your understanding
Answer these without looking back at the article.
- How do you fix the Square-Rectangle LSP violation?
- What is the difference between is-a and behaves-like-a?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.