The Liskov Substitution Principle (LSP) states that objects of a superclass should be replaceable with objects of its subclass without altering the correctness of the program. This principle is crucial for ensuring that derived classes extend the behavior of the base class without introducing unexpected issues.
Key Idea
A subclass should:
- Behave in a way consistent with its superclass.
- Not override or weaken the expectations of the base class.
If a subclass cannot fully substitute for its base class, it violates LSP.
Real-Life Example
Imagine a Restaurant program that calculates areas of shapes.
Violation of LSP
javaclass Bird { void fly() { System.out.println("I am flying!"); } } class Sparrow extends Bird { // Sparrow can fly, so no issue } class Penguin extends Bird { // Penguins cannot fly, but they are forced to implement fly() @Override void fly() { throw new UnsupportedOperationException("Penguins can't fly!"); } } public class Main { public static void main(String[] args) { Bird sparrow = new Sparrow(); sparrow.fly(); // Works fine Bird penguin = new Penguin(); penguin.fly(); // Throws exception! Violates LSP } }
Fixing the Violation (Applying LSP)
java// Base interface for all birds interface Bird { void eat(); } // Separate interface for birds that can fly interface FlyingBird extends Bird { void fly(); } // Sparrow can fly, so it implements FlyingBird class Sparrow implements FlyingBird { public void eat() { System.out.println("Sparrow is eating."); } public void fly() { System.out.println("Sparrow is flying."); } } // Penguin does not implement FlyingBird, only Bird class Penguin implements Bird { public void eat() { System.out.println("Penguin is eating."); } } public class Main { public static void main(String[] args) { FlyingBird sparrow = new Sparrow(); sparrow.fly(); // Works fine Bird penguin = new Penguin(); // penguin.fly(); // This line doesn't even compile, preventing errors } }
Now, only birds that can fly implement the fly()
method.Penguin
does not inherit fly()
, so no exceptions or unexpected behavior.
The code follows proper abstraction and inheritance, keeping it flexible and correct.