Intuition The one core idea
A subclass is a promise : "hand me anywhere a base class was expected, and nothing breaks." Every symbol on the parent page — T , S , ϕ , the pre/post arrows — exists to make that promise precise enough to check.
This page assumes you know nothing . Before you can read SOLID — Liskov Substitution Principle , you must be able to read six little pieces of vocabulary. We build each one from a plain-words meaning, anchor it to a picture, and say exactly why the topic needs it.
Everything in LSP is a three-way story between:
a base class — a general kind of thing (e.g. Bird),
a subclass — a more specific version of it (e.g. Penguin),
a caller — some other code that was written to use the base, and never heard of the subclass.
A class is a blueprint that bundles data (fields) with behaviour (methods). Picture a rubber stamp: the stamp is the class, each stamped shape on paper is an object (one instance).
A method is a named action a class can perform, like fly() or setWidth(w). In the figure above it is the arrow labelled "does something".
Why the topic needs these: LSP is entirely about whether a subclass's methods behave like the base's methods. No classes, no methods, no LSP.
Inheritance means a subclass automatically gets all the fields and methods of its base, and may replace some of them with its own version. In Java we write class Penguin extends Bird.
Draw an arrow from the child up to the parent. That arrow means "is a kind of, and inherits from." The parent sits above; the specialised children hang below it.
Why the topic needs it: LSP is a rule about what inheritance is allowed to do . Without inheritance there is no subclass to substitute.
To override a method is for a subclass to supply its own version of a method the base already had, keeping the same name. Penguin.fly() overrides Bird.fly().
Common mistake Overriding vs overloading
Why they blur: both change what a method call does.
The fix: Overriding = same name, same inputs, subclass replaces the base version (this is what LSP polices). Overloading = same name, different inputs, a totally separate method. LSP only cares about overriding.
Why the topic needs it: every LSP violation on the parent page is an override that misbehaves — Square.setWidth, Penguin.fly. The danger lives in the override.
Polymorphism ("many shapes") is when a single call written against the base runs whichever subclass's body is really there at runtime. You write bird.fly() once; it runs Penguin.fly() or Sparrow.fly() depending on the actual object.
Why this tool and not if-checks? The whole payoff of inheritance is writing code once and trusting all subtypes. If you had to write if (bird is Penguin) ... everywhere, you'd gain nothing. LSP is precisely the condition under which polymorphism is safe. See Polymorphism .
A contract is the deal a method offers its caller: "if you give me valid inputs, I promise a valid result." Three words describe it.
A precondition is what must be true before you call a method for it to work — the caller's duty. For setWidth(w) a precondition might be "w > 0 ".
A postcondition is what the method promises will be true after it returns — the method's duty. For setWidth(5) a postcondition is "width is now 5, height unchanged".
An invariant is something that stays true the whole time an object lives — before and after every method. For a Rectangle, "width and height are independent" is an invariant.
Pre = caller's promise (the entry fee). Post = method's promise (the receipt). Invariant = house rule that never turns off.
Why the topic needs them: the parent's three rules are entirely stated in these words. This whole vocabulary comes from Design by Contract . LSP = "a subclass must not break the base's contract."
Now Liskov's formal sentence becomes readable.
So Liskov's line —
ϕ ( x ) true for all x : T ⟹ ϕ ( y ) true for all y : S
reads in plain words: "Any true fact about base objects must stay true for subclass objects." That's the one-sentence idea, dressed in symbols.
Intuition Why an arrow and not "equals"?
We do not need the subclass to be identical to the base — only to keep every promise. Implication (⟹ ) is one-directional: "base-truth forces sub-truth." That asymmetry is exactly why preconditions and postconditions point in opposite directions on the parent page.
Let pre and post be the contract pieces. The parent's two arrows now decode cleanly:
pre base ⟹ pre sub ( sub must accept ≥ base’s inputs )
post sub ⟹ post base ( sub must promise ≥ base’s outputs )
Contract pre post invariant
Liskov Substitution Principle
Related principles that lean on the same foundations: SOLID — Open Closed Principle (needs polymorphism), SOLID — Interface Segregation Principle (fixes fat contracts), SOLID — Dependency Inversion Principle , SOLID — Single Responsibility Principle .
What is a class A blueprint bundling data (fields) with behaviour (methods); each stamped copy is an object.
What is a method A named action a class can perform.
What does extends / inheritance give a subclass All the base's fields and methods, with the option to override some.
Overriding vs overloading Overriding = same name AND same inputs, replacing the base version; overloading = same name but different inputs.
What is polymorphism One call written against the base runs whichever subclass's body is actually present at runtime.
What is a precondition What must be true before a call — the caller's duty.
What is a postcondition What the method promises after it returns — the method's duty.
What is an invariant A fact that stays true the whole life of the object.
What does S <: T mean S is a subtype of T : every S can stand in wherever a T was expected.
What does ϕ ( x ) mean A property (true fact) about object x .
What does ⟹ mean, and why one-directional here "Implies"; the subclass need not equal the base, only keep every base-truth, so the arrow only points base to sub.