Liskov Substitution Principle
Anywhere a supertype works, every subtype must work too.
The violation
Making the free sample “a kind of book” because it shares a few fields:
class FreeSampleChapter extends Book {
@Override
BigDecimal price() {
throw new UnsupportedOperationException("samples can't be bought!");
}
}
Now every piece of code handling books – totals, receipts, recommendations – can blow up at
runtime on a subtype it was promised would behave like a Book. Callers start adding
instanceof FreeSampleChapter guards, which is the design admitting the inheritance was a lie.
The fix
Subtyping follows behaviour, not field overlap. PrintedBook and
Ebook genuinely keep the Purchasable contract, so
Till.totalOf works on any mix of them with no guards.
FreeSampleChapter simply isn’t Purchasable – the compiler stops it
reaching the till, instead of an exception stopping the sale.
Benefits
- Trustworthy polymorphism – code written against the interface works for every implementation, present and future.
- Errors move from runtime to compile time – an unbuyable item in a basket is a type error, not a production incident.
- No
instanceoflitter – callers never need to know which subtype they hold.