Skip to main content
Category: Principles
Type: Systems design principle
Origin: Barbara Liskov, OOPSLA keynote Data Abstraction and Hierarchy, 1987
Also known as: LSP; behavioral subtyping; strong behavioral subtyping
Quick Answer — The Liskov Substitution Principle says a subtype may stand in for its parent only if it keeps the parent’s behavioral contract, so clients do not have to know which object they received. Barbara Liskov stated the substitution property in a 1987 OOPSLA keynote; she and Jeannette Wing formalized it in 1994. The practical test is simple: if you must inspect the concrete kind before calling a parent method, the hierarchy is already lying.

What is the Liskov Substitution Principle?

The Liskov Substitution Principle is a design rule that a subtype must be usable wherever its parent type is expected, without breaking the correctness those clients already rely on.
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
That is not a compiler check. Compilers match names and signatures. The principle is about promises: what callers of the parent may assume will still be true. The everyday picture is a substitute teacher. The timetable still says “math, period two.” A substitute who can run that period is substitutable. A substitute who will not take attendance, or who can teach only if the regular teacher sits in the room, is not. The school should not have to rewrite the day because the person in the chair changed. In code, Square-extends-Rectangle is the usual bruise. A Rectangle promises that changing width need not change height. A Square that keeps sides equal must change both. Code that sizes a Rectangle and then reads height will be surprised. The family tree said “is-a.” The contract did not. The Open-Closed Principle needs this rule: you cannot plug in a new subtype if old clients have to inspect which subtype they got.

Liskov Substitution Principle in 3 Depths

  • Beginner: If it claims to fill a role, it must do the jobs the role already advertised. A cell that fits an AA slot but delivers a different voltage is the same shape, not the same contract.
  • Practitioner: Before you inherit, list the parent’s promises—what callers may pass, what they may expect back, what never happens. A subtype may be extra-generous. It may not be extra-fussy.
  • Advanced: Behavioral subtyping is a history constraint as well as a snapshot. A mutable object must not introduce state changes the parent ruled out. That is why a stack you can poke in the middle is not a stack, even if it also has push and pop.

Origin

The Liskov Substitution Principle began as a question about type hierarchy, not as a slogan on a team wall. Barbara Liskov, then at the MIT Laboratory for Computer Science, gave the keynote Data Abstraction and Hierarchy at OOPSLA ‘87 in Orlando (4–8 October 1987). The talk was published in the conference addendum (ACM, doi 10.1145/62138.62141) and again in ACM SIGPLAN Notices 23(5): 17–34 (May 1988). She had built data abstraction in the CLU language in the 1970s. At OOPSLA she argued that Smalltalk-style inheritance of implementation is not the same as a type hierarchy that clients can trust. A paper she had read treated stacks and queues as subtypes of each other. That cannot work: code written for last-in-first-out behavior fails if it is handed first-in-first-out instead. She and Jeannette Wing gave the idea its formal teeth in A Behavioral Notion of Subtyping, ACM Transactions on Programming Languages and Systems 16(6): 1811–1841 (November 1994, doi 10.1145/197320.197383). Their subtype requirement: any property you can prove about objects of type T must still hold for objects of a subtype S. Subtyping here is a relation between specifications, not between class files. Robert C. Martin brought the idea into everyday object-oriented practice. In the March 1996 C++ Report, his Engineering Notebook column was titled “The Liskov Substitution Principle.” His working line was:
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
Martin’s 2003 book Agile Software Development: Principles, Patterns, and Practices (Prentice Hall) kept that test. Around 2004, Michael Feathers arranged Martin’s “first five” principles into the SOLID mnemonic. The Liskov Substitution Principle is the “L.” Liskov received the 2008 ACM Turing Award for data abstraction, fault tolerance, and distributed computing. The award is broader than this one rule. The name stuck in engineering talk all the same.

Key Points

The Liskov Substitution Principle earns its keep when a new kind of thing must fill an old slot. It is not a ban on extra features. It is a rule about which promises you are not allowed to take back.
1

Is-a is a contract, not a family resemblance

Sharing a few fields or a convenient parent class does not make a subtype. Callers of the parent have a right to the advertised behavior, not to your inheritance diagram. A penguin that inherits fly() and then throws is not “a bird with a special case.” It is a parent type that claimed too much. Separation of concerns tells you to split the jobs. This principle tells you not to glue them back together with a fake family tree.
2

Do not make the door narrower, or the result weaker

A subtype may weaken preconditions (accept more inputs) and strengthen postconditions (guarantee more about outputs). It may not reverse those arrows. If the parent said “any positive amount,” a child that rejects amounts under 100 is not substitutable. If the parent said “returns a non-empty list,” a child that returns empty is not substitutable. Extra methods are fine. Quieter, fussier versions of the old methods are not.
3

Invariants and history have to survive the swap

Whatever the parent said remains true of every valid object must remain true after substitution. If a property list is supposed to hold only strings, a subtype that can be stuffed with integers through a parent method has already broken the invariant. History matters too: if the parent never let you shrink a value, a child that lets setWidth also cut height is changing a past that callers thought was closed.
4

If clients must inspect the concrete kind, the hierarchy already failed

Chains of if on the runtime type, or comments that say “do not call this on the special subclass,” are the smell. Those clients now change every time a new cousin arrives, which is how this principle and the Open-Closed Principle fail together. The Single Responsibility Principle often sits underneath: one type was carrying two jobs, and inheritance was used as glue.

Applications

Use the Liskov Substitution Principle when one slot will be filled by several kinds of thing, and old clients should keep working without reading a warning label. Do not use it to force every similar noun onto one parent.

Split a lying hierarchy before you add the next cousin

If Square must keep equal sides, it is not a Rectangle you can resize freely. If a fixed-term deposit cannot be withdrawn, it is not an Account whose parent promised withdraw. Make a slimmer parent—or no parent—and give each kind the methods it can actually honor. A teaching assistant’s grading types follow the same cut: a “quiz” that cannot be rescored is not a “gradable item” that the rest of the script assumes is editable.

Check an acting role against the job, not the badge

An acting manager who cannot sign purchase orders is not substitutable for the manager the process already depends on. Either give the deputy the same signing rights, or change the process so signing is not part of the slot. Job titles are inheritance diagrams. The workflow is the contract. Surprise here is a least astonishment failure as well as a substitution failure.

Write a club office that a substitute can actually hold

A school or community group often names a “treasurer” and then discovers the backup cannot access the bank, file the report, or hold the key. The charter promised a role. The backup filled a name. List the jobs the office must do—report money, keep the key, attend the meeting—and only then ask who can stand in. A rota that needs the original person in the room is not a rota.

Treat “compatible” public forms as contracts, not lookalikes

A substitute ID, a “same as” tax schedule, or a drop-in permit that clerks cannot process in the usual window is not substitutable. Citizens pay the mismatch in extra visits. If the published form says any matching attachment will do, every matching attachment must actually do. If some will not, print the restriction on the parent form; do not hide it in a subtype that still uses the same title.

Case Study

The numbered window onto the Liskov Substitution Principle as an operating wound is Java’s Properties class—not a claim that one library class is the whole of type theory. From JDK 1.0 (1996), java.util.Properties has represented a persistent string-to-string list that can be loaded from and stored to a stream. It was written as a subclass of Hashtable, which accepts any non-null keys and values. The published contract of the parent is wider than the published job of the child. Because of that inheritance, put and putAll can be called on a Properties object. The official Java API still says their use is “strongly discouraged,” because they let callers insert non-string keys or values. If store or list is then called on that “compromised” object, the call fails—typically with a class-cast error. The child did not take back the parent method. It inherited a door it cannot honor. People have been pointing at this for decades. On 17 May 2016, OpenJDK bug JDK-8157123 was filed. Its title named the Liskov Substitution Principle directly. The suggested fix was composition: hold a table inside Properties, do not be a Hashtable. The bug was closed Won’t Fix. A published type relationship is a promise to existing callers; dropping the extends clause would have been a compatibility break. In JDK 9 (2017), the class was reworked so that it no longer stores entries in the inherited table. Release note JDK-8175789 records that values live in an internal concurrent map, and that getters were de-synchronized to reduce deadlocks. The comment in current OpenJDK source still reads that Properties does not store values in its inherited Hashtable. The class still extends Hashtable. As of 2026, the hierarchy lie remains in the public API. The case shows the principle as a lock-in, not a style preference. Once you publish “this is-a that,” you can change the guts and you can write warnings. You cannot quietly take the substitution back. Boundary note: Properties still works if you use setProperty and keep strings. The failure is the type claim, not every call site. The lesson for new design is the 2016 workaround that could not be adopted: compose the table; do not inherit it.

Boundaries and Failure Modes

The Liskov Substitution Principle fails when the parent contract is already a grab-bag. Java’s List “optional operations,” where add may throw on a fixed-size or unmodifiable list, are a library-wide compromise. Every implementer is then a partial subtype. Clients must read footnotes. That is not a license to add more footnotes; it is a warning that the parent was drawn too wide. Slim the parent, or split the interfaces, before you pile on another cousin that throws. It also fails as a retrofit on a published lie. Properties is the exhibit: the right design is composition, and the right design is no longer available. Calling a twenty-year-old hierarchy “a learning opportunity” does not make it safe to copy. Copy the 2016 workaround, not the 1996 extends clause. The common misuse is a God type with NotSupported on half the methods. Birds that cannot fly, accounts that cannot withdraw, and devices that cannot power off are not “specialized subtypes.” They are extra-fussy children of an oversized parent. Another misuse is inheritance for code reuse when the behaviors diverge. That reuse belongs behind a shared helper, which is DRY without a fake is-a. Unexpected exceptions from a subtype also fight fail fast: the program does fail, but at a call the parent had promised was safe.

Common Misconceptions

The English name collides with “if it compiles it substitutes,” with “throwing not-supported is fine,” and with SOLID as a personality test for classes.
Compilers check names, types, and visibility. They do not check that setWidth still means what callers thought, or that a property list still holds strings. Behavioral subtyping is a claim about programs that already work. A green build is necessary and nowhere near sufficient.
If the parent advertised that the method is part of ordinary use, throwing is taking the promise back. Optional operations in a standard library are a known stain, not a pattern to copy. Either the method does not belong on the parent, or every subtype must honor it. A comment that says “do not call this here” is a contract hole, not a design.
Liskov’s 1987 talk used objects because that was the conference. The same shape appears in roles, APIs, documents, and deputies. If a slot says “any treasurer,” “any attachment,” or “any payment method,” the stand-in must keep that slot’s jobs. A JSON field that is “compatible” but breaks old readers is the same failure without a class keyword.
These pages sit next to the same problem: how to let a new kind of thing fill an old slot without surprising the people who already depend on that slot.

Open-Closed Principle

New behavior arrives as a new piece. This principle is the test that the new piece can actually sit in the old slot.

Single Responsibility Principle

One reason to change per module. Fake subtypes often appear when one parent is carrying two jobs.

Separation of Concerns

Keep different decisions apart. A slimmer parent is often a cleaner cut, not a deeper tree.

Principle of Least Astonishment

A substitute that compiles but behaves oddly is an astonishment by construction.

Fail Fast

A subtype that throws where the parent was safe fails late, in someone else’s call.

Law of Demeter

Talk to your immediate neighbors. Clients that inspect a cousin’s internals are already bypassing the parent contract.

One-Line Takeaway

If a new piece needs a warning that clients must not treat it as the parent, it is not a subtype—slim the parent, or compose the extra behavior beside it.