The Liskov Substitution Principle (LSP) states:
A subclass should be able to replace its parent class without changing the correctness of the program.
In simple words:
A child class should behave like its parent class without causing unexpected errors or changing the expected behavior.
The application has a Vehicle class with a startEngine() method.
Car and Bike work correctly because they have engines.
However, Bicycle also extends Vehicle, even though it doesn't have an engine.
To satisfy inheritance, Bicycle throws an exception inside startEngine().
throw new UnsupportedOperationException("Bicycle has no engine");As a result, replacing a Vehicle with a Bicycle causes the application to fail.
The design is improved by separating vehicles based on their behavior.
Vehicle→ Abstract class for all vehiclesEngineVehicle→ Abstract class for vehicles with enginesCar→ Engine vehicleBike→ Engine vehicleBicycle→ Non-engine vehicle
Now every subclass behaves correctly and can safely replace its parent without breaking the application.
c_LSP
│
├── before/
│ └── LSP Violation
│
├── after/
│ └── LSP Implementation
│
└── README.md
- Prevents unexpected runtime errors
- Creates proper inheritance hierarchies
- Improves code reliability
- Supports polymorphism correctly
- Makes code easier to maintain
Think of a vehicle rental company.
All vehicles can move, but not every vehicle has an engine.
- 🚗 Car → Can move and start an engine
- 🏍 Bike → Can move and start an engine
- 🚲 Bicycle → Can move but has no engine
Instead of forcing every vehicle to implement startEngine(), only engine-powered vehicles should have that behavior.
This creates a correct inheritance hierarchy and follows the Liskov Substitution Principle.
A subclass should extend the behavior of its parent, not break it.