Skip to main content

🔄 The LSP Rule

“Objects of a superclass should be replaceable with objects of its subclasses without breaking the application.”
In simple terms: If you expect a Bird, any type of bird (Sparrow, Eagle, Parrot) should work the same way!
The Test: If your code works with a parent class, it should work with ANY child class without knowing the difference!
The rental car analogy: When you rent a car, you expect it to have a steering wheel, pedals, and an ignition. Whether you get a Toyota Camry or a BMW 3 Series, the basic driving interface works the same. You would be shocked if you got a vehicle where pressing the brake made it accelerate. That is LSP: any subclass must honor the behavioral contract of its parent. The caller should never need to check “wait, which specific type did I get?” before using it safely.

🐧 The Famous Penguin Problem

The classic LSP violation:
The Problem: The code expects ALL birds to fly, but Penguin breaks that expectation!

✅ The Correct Solution


📏 The Rectangle-Square Problem

Another famous LSP violation:

❌ BAD: Square extends Rectangle

✅ GOOD: Separate Shapes

Key insight: “Is-a” in mathematics does not always mean “is-a” in software. A square is a rectangle mathematically, but in code, the behavioral contract (independently mutable width and height) breaks for squares. LSP teaches you to think about behavioral compatibility, not just conceptual taxonomy.

🎯 LSP Rules to Follow

Rule 1: Method Signatures

Child methods should accept the same or broader input types:

Rule 2: Return Types

Child methods should return the same or narrower types:

Rule 3: No New Exceptions

Children shouldn’t throw exceptions parents didn’t:

Rule 4: Honor Invariants

If parent guarantees something, child must too:

🎮 Real Example: File System

❌ BAD: ReadOnlyFile breaks File contract

✅ GOOD: Separate Interfaces


🐾 Real Example: Pet Store


🧪 Practice Exercise

This code violates LSP. Fix it!

📝 Quick LSP Checklist


Interview Insight

LSP is the hardest SOLID principle to demonstrate in interviews, but it creates the strongest impression when you do. The typical way it surfaces: you design a class hierarchy, and the interviewer probes an edge case. “What about a read-only user — can they inherit from User?” If the User parent class has a save() method and ReadOnlyUser throws an exception on save, that is an LSP violation. The strong answer: “I would not make ReadOnlyUser extend User because it cannot honor the save() contract. Instead, I would create a Viewable interface that both share, and only give the writable path to full User objects.” The moment you use the phrase “honor the behavioral contract of the parent,” you signal deep understanding. The practical test: anywhere your code does isinstance checks to handle a specific subclass differently, that is a sign LSP might be violated — you are compensating for a subclass that does not truly substitute.

🏃 Next: Interface Segregation Principle

Let’s learn why smaller, focused interfaces are better than big ones!

Continue to Interface Segregation →

Learn why many small interfaces beat one big interface!