The Liskov Substitution Principle (LSP) is the "L" in SOLID. It ensures that you can replace a parent object with its child without breaking your application. If a subclass changes the behavior in unexpected ways, it violates the principle.
The "Duck Test"
If you have to check for batteries before feeding it, it's not a true substitute for a duck.
Turing Award Winner
Introduced LSP in 1987
"If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."
Objects of a subclass should be replaceable with objects of their superclass without breaking the application. The subclass should not "narrow" the behavior or throw unexpected errors for operations the parent supports.
The most famous violation of LSP. Mathematically, a square is a rectangle. But in Object-Oriented Programming, inheritance implies behavior, not just geometry.
We have a function resize(Rectangle r) that sets the width to 50 and height to 100. It expects the resulting Area to be 5000.
r.setWidth(50)
r.setHeight(100)
Instead of inheriting, both Square and Rectangle should implement a common interface Shape that only guarantees getArea().
This prevents the user from assuming they can independently set width and height on any Shape.
interface Shape { getArea(): number; } class Rectangle implements Shape { constructor(public w, public h) {} getArea() { return this.w * this.h; } } class Square implements Shape { constructor(public size) {} getArea() { return this.size ** 2; } }
The resize() function can no longer exist for a generic Shape. The compiler (or logic) prevents you from writing code that assumes width and height are decoupled. The "bad usage" is now impossible.
Visual Scale: 1px = 2 units
The Contract: Any device plugging into USB must handle 5V power and data transfer.
The Violation: A "USB Coffee Maker" that draws 220V. Even if it fits the port (Interface), it violates the electrical contract, frying the motherboard.
"Just because it fits the interface doesn't mean it respects the contract."
The Contract: Pressing the red button turns off the device.
The Violation: You replace the TV remote with a Bomb remote. It looks the same (IS-A Remote), but pressing the red button explodes the house instead of turning off the TV.
"Subtypes must not change the purpose of the base class methods."
A subclass cannot enforce stricter rules on input than the parent. If the parent accepts any number, the child cannot demand only positive numbers.
Example: If feed(Animal a) works for all animals, you can't pass a Tiger that requires specific premium meat or throws an error.
A subclass must deliver the same outcomes as the parent. If the parent promises to return a valid User object, the child cannot return null.
Example: Our Rectangle/Square. The parent implies setW doesn't touch height. The square violates this outcome.
1. You have a class `Bird` with a method `fly()`. You create a subclass `Penguin` that throws an error when `fly()` is called. Does this violate LSP?