How would you fix an LSP violation?
There isn't a single solution. The fix depends on the design. Common approaches are extracting behavior into an interface, redesigning the parent class, splitting the inheritance hierarchy, using…
There isn’t a single solution. The fix depends on the design. Common approaches are extracting behavior into an interface, redesigning the parent class, splitting the inheritance hierarchy, using composition instead of inheritance, or removing inheritance altogether. The key is that every subclass must honor the contract defined by the parent.
A Simple Rule to Remember
When designing inheritance, ask yourself:
“Can every child honestly do everything the parent promises?”
- Yes → Inheritance is appropriate.
- No → Rethink the design (interface, composition, hierarchy, or contract).
That’s the essence of the Liskov Substitution Principle.
There is no single fix for an LSP violation. The correct solution depends on why the inheritance exists and what the real-world domain looks like.
The important idea is:
If a child cannot fulfill the parent’s contract, then the inheritance hierarchy is wrong.
Let’s look at different ways to solve the Bird and Penguin example.
Solution 1 (Most Common) – Move the behavior to a separate interface ✅
Not every bird can fly.
So don’t put fly() inside Bird.
class Bird {
public void eat() {
System.out.println("Bird is eating");
}
}
Create a separate interface.
interface Flyable {
void fly();
}
Flying birds:
class Sparrow extends Bird implements Flyable {
@Override
public void fly() {
System.out.println("Sparrow is flying");
}
}
Penguin:
class Penguin extends Bird {
}
Now,
Flyable bird = new Sparrow();
bird.fly();
No problem.
This is considered the best solution because the design now models reality correctly.
Solution 2 – Split the hierarchy
Instead of
Bird
↑
Penguin
Create two parent classes.
Bird
↑
FlyingBird
↑
Sparrow
Bird
↑
NonFlyingBird
↑
Penguin
Example
class Bird {
}
class FlyingBird extends Bird {
public void fly() {
System.out.println("Flying");
}
}
class Penguin extends Bird {
}
Now Penguin never inherits fly().
Solution 3 – Use Composition Instead of Inheritance
Sometimes inheritance is the wrong choice.
Instead of
Penguin extends Bird
Use
Penguin HAS-A Bird
Example
class Bird {
public void eat() {
System.out.println("Eating");
}
}
class Penguin {
private Bird bird = new Bird();
public void swim() {
System.out.println("Swimming");
}
public void eat() {
bird.eat();
}
}
No incorrect inheritance.
Solution 4 – Redesign the Parent Contract
Sometimes the parent itself is poorly designed.
Instead of
class Bird {
public void fly() {
}
}
Make it
abstract class Bird {
public abstract void move();
}
Now
class Sparrow extends Bird {
@Override
public void move() {
System.out.println("Flying");
}
}
class Penguin extends Bird {
@Override
public void move() {
System.out.println("Walking and Swimming");
}
}
Every bird moves.
No violation.
Solution 5 – Remove the Inheritance
Sometimes the classes simply aren’t in an “is-a” relationship for the behavior you’ve modeled.
Bird
Penguin
Sparrow
No inheritance at all.
This is less common but sometimes correct.
Which Solution Should You Choose?
| Situation | Best Solution |
|---|---|
| Only some subclasses have a behavior (e.g., fly) | ✅ Extract an interface (Flyable) |
| Parent class is designed incorrectly | ✅ Redesign the parent contract |
| Inheritance doesn’t represent reality | ✅ Use composition instead of inheritance |
| Need separate categories | ✅ Split the hierarchy |
| Inheritance adds no value | ✅ Remove inheritance |