Chapter 100·Beginner·8 min read
Inheritance: Defining a Type in Terms of Another
A plain-English, language-agnostic guide to inheritance in OOP — how a subclass reuses and extends a parent, when an 'is-a' relationship genuinely fits, and why inheritance is the pillar most likely to be overused.
July 15, 2026
The first two pillars — encapsulation and abstraction — are about a single object's boundary. Inheritance is the first pillar about relationships between types: defining one type in terms of another. It's the third of the four pillars, and the one you should reach for most carefully.
What inheritance is
Inheritance lets a class reuse and extend another. A SavingsAccount is an Account — it has everything an account has, plus an interest rate — so instead of rewriting the shared parts, it inherits them and adds only what's new:
class Account:
balance = 0
method deposit(amount): ...
method withdraw(amount): ...
class SavingsAccount extends Account:
interestRate = 0.03
method addInterest():
deposit(balance * interestRate) # reuses inherited deposit()SavingsAccount gets balance, deposit, and withdraw for free, and adds addInterest. The parent is the superclass (or base class); the child is the subclass.
The test: is it genuinely "is-a"?
Inheritance models an "is-a" relationship, and that phrase is the test. Say it out loud:
- "A savings account is an account." ✅ Sensible.
- "An account is a transaction log." ❌ No — an account has a log. That's a different relationship (composition), and forcing inheritance here would be a mistake.
If you can't honestly complete "X is a kind of Y", inheritance is the wrong tool — even if it happens to let you reuse some code.
Overriding: changing inherited behaviour
A subclass can override an inherited method — replace the parent's version with its own. A CheckingAccount might override withdraw to allow going negative up to an overdraft limit:
class CheckingAccount extends Account:
overdraftLimit = 500
method withdraw(amount): # overrides Account.withdraw
if amount <= balance + overdraftLimit:
balance = balance - amountOverriding is what lets different subclasses behave differently through the same method name — which is the seed of the next pillar, polymorphism.
Why inheritance is the most overused pillar
Inheritance removes duplication when types genuinely share structure. But it's the pillar beginners reach for too often, and it has a real cost: a subclass is tightly bound to its parent.
A useful warning sign: if you're inheriting purely to reuse some code — not because a true "is-a" relationship exists — you're using the wrong tool. That exact situation is what the final chapter, composition over inheritance, is about.
Recap
- Inheritance defines one type in terms of another: a subclass reuses and extends a superclass.
- It models an "is-a" relationship — use the "is a kind of" test before reaching for it.
- Overriding lets a subclass replace inherited behaviour, which sets up polymorphism.
- It's the most overused pillar: subclasses are tightly coupled to parents, and deep trees become brittle (the fragile base class problem). Inherit for genuine "is-a", not just to reuse code.
Overriding hinted at something powerful: many types responding to the same call in their own way. That's the next pillar. Continue to Polymorphism.