> ## Documentation Index
> Fetch the complete documentation index at: https://meta.niceshare.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Liskov Substitution Principle

> Liskov Substitution Principle says a subtype must keep the parent contract so clients need not know which object they got. Origin, Java Properties, limits.

<Info>
  **Category**: Principles<br />
  **Type**: Systems design principle<br />
  **Origin**: Barbara Liskov, OOPSLA keynote *Data Abstraction and Hierarchy*, 1987<br />
  **Also known as**: LSP; behavioral subtyping; strong behavioral subtyping
</Info>

<Note>
  **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.
</Note>

## 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](/principles/open-closed) 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.

<Steps>
  <Step title="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](/principles/separation-of-concerns) tells you to split the jobs. This principle tells you not to glue them back together with a fake family tree.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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](/principles/open-closed) fail together. The [Single Responsibility Principle](/principles/single-responsibility) often sits underneath: one type was carrying two jobs, and inheritance was used as glue.
  </Step>
</Steps>

## 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.

<CardGroup cols={2}>
  <Card title="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.
  </Card>

  <Card title="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](/principles/least-astonishment) failure as well as a substitution failure.
  </Card>

  <Card title="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.
  </Card>

  <Card title="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.
  </Card>
</CardGroup>

## 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](/principles/dry-principle) without a fake is-a. Unexpected exceptions from a subtype also fight [fail fast](/principles/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.

<AccordionGroup>
  <Accordion title="If the subclass compiles against the parent, the Liskov Substitution Principle is satisfied">
    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.
  </Accordion>

  <Accordion title="A subtype may throw unsupported on methods it does not like">
    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.
  </Accordion>

  <Accordion title="The Liskov Substitution Principle is only about object-oriented classes">
    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.
  </Accordion>
</AccordionGroup>

## Related Concepts

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.

<CardGroup cols={3}>
  <Card title="Open-Closed Principle" href="/principles/open-closed">New behavior arrives as a new piece. This principle is the test that the new piece can actually sit in the old slot.</Card>
  <Card title="Single Responsibility Principle" href="/principles/single-responsibility">One reason to change per module. Fake subtypes often appear when one parent is carrying two jobs.</Card>
  <Card title="Separation of Concerns" href="/principles/separation-of-concerns">Keep different decisions apart. A slimmer parent is often a cleaner cut, not a deeper tree.</Card>
  <Card title="Principle of Least Astonishment" href="/principles/least-astonishment">A substitute that compiles but behaves oddly is an astonishment by construction.</Card>
  <Card title="Fail Fast" href="/principles/fail-fast">A subtype that throws where the parent was safe fails late, in someone else’s call.</Card>
  <Card title="Law of Demeter" href="/principles/law-of-demeter">Talk to your immediate neighbors. Clients that inspect a cousin’s internals are already bypassing the parent contract.</Card>
</CardGroup>

## One-Line Takeaway

<Tip>
  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.
</Tip>
