Inheritance
Imagine you have Dog , Cat , and Bird classes. Each needs a name , age , eat() , and sleep() . Without inheritance, you'd copy-paste the same code three times. Inheritance lets you define shared behavior once in a parent class ( Animal ), and every child class automatically gets it. Write once, reuse everywhere.
Learn Inheritance in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
- ✅ What inheritance is and why it eliminates code duplication
- ✅ The extends keyword — creating child classes
- ✅ The super keyword — calling parent constructors and methods
- ✅ Method overriding — replacing parent behavior in child classes
- ✅ @Override annotation — safety net for overriding
- ✅ Polymorphism — same method name, different behavior per type
- ✅ instanceof — checking an object's type at runtime
- ✅ Inheritance vs Composition — the critical design decision
💡 Real-World Analogy: Family Tree
Think of a family tree. A child inherits traits (eye color, height) from their parents, but can also develop unique traits of their own. Similarly, a child class inherits fields and methods from a parent class, but can add new ones or override inherited ones with different behavior.
1️⃣ The Problem Inheritance Solves
Without inheritance, you'd duplicate code across similar classes:
🔑 The extends keyword: class Dog extends Animal means "Dog is a type of Animal and inherits all its fields and methods." Dog automatically gets name , age , eat() , and sleep() without writing them again.
2️⃣ The super Keyword — Talking to the Parent
super refers to the parent class. You use it in two main places:
⚠️ Critical rule: If the parent class has no default (no-argument) constructor, you must call super(...) as the very first statement in the child's constructor. The compiler will refuse to compile otherwise.
3️⃣ Method Overriding — Customizing Inherited Behavior
A child class can override a parent's method to provide its own implementation. The method signature (name, parameters, return type) must match exactly. Always use the @Override annotation for safety.
💡 Why @Override ? Without it, a typo like speek() would create a new method instead of overriding speak() — a silent, hard-to-find bug. @Override tells the compiler to verify the parent actually has this method.
4️⃣ Polymorphism — "Many Forms"
Polymorphism is the most powerful concept in OOP. It means a parent type variable can hold any child type object, and method calls automatically use the child's version. Think of it like a universal TV remote — the "power" button does something different on every TV brand.
🔑 Why this is powerful: You can write methods that accept Animal and they automatically work with Dog, Cat, Bird, and any future animal class you create. You don't need to change the method — it just works. This is the foundation of extensible software.
5️⃣ instanceof — Type Checking at Runtime
Sometimes you have a parent type variable but need to know the actual type at runtime. Use instanceof :
6️⃣ Inheritance vs Composition
This is one of the most important design decisions you'll make. The test is simple:
Inheritance ("is-a") ✅
Composition ("has-a") ✅
❌ Bad inheritance: Stack extends ArrayList — a Stack is NOT a type of ArrayList. It just happens to use a list internally. Use composition instead.
💡 Pro rule: "Favor composition over inheritance." Deep inheritance hierarchies (5+ levels) become rigid and hard to change. Use 1-2 levels of inheritance + interfaces for flexibility.
7️⃣ Access Modifiers in Inheritance
Not all parent members are accessible to child classes. Here's what each access modifier allows:
Modifier
Same Class
Subclass
Other Classes
public
✅
protected
❌
(default)
Same pkg
private
Use protected for fields/methods that subclasses need but outsiders shouldn't access. It's the sweet spot for inheritance.
Common Mistakes
- ❌ Forgetting super() in child constructor: If the parent has no default constructor, you MUST explicitly call super(...) as the first line.
- ❌ Forgetting @Override : A typo like speek() creates a new method instead of overriding speak() . The annotation catches this at compile time.
- ❌ Overusing inheritance: Don't use inheritance just for code reuse. Use it only for true "is-a" relationships. If it's "has-a", use composition.
- ❌ Deep inheritance hierarchies: More than 3 levels deep becomes confusing and rigid. Keep it shallow.
- ❌ Calling overridden methods in constructors: This can cause bugs because the child class isn't fully initialized yet when the parent constructor runs.
Pro Tips
💡 Always use @Override: It's free compile-time error checking. There's no reason not to use it.
💡 Keep hierarchies shallow: 1-2 levels of inheritance is ideal. Beyond 3, consider interfaces + composition.
💡 Program to interfaces, not implementations: Use Animal pet = new Dog() instead of Dog pet = new Dog() when possible — this makes your code more flexible.
💡 Use protected wisely: It's better than public for fields that subclasses need, but consider if a getter method would be even better.
💡 The Liskov Substitution Principle: A child class should work anywhere the parent class is expected. If a method expects Animal , passing a Dog should work correctly.
📋 Quick Reference
Keyword
Syntax
Purpose
extends
class Dog extends Animal
Inherit from parent
super()
super(name, age)
Call parent constructor
super.method()
super.speak()
Call parent's method version
@Override
@Override void speak()
Override with safety check
instanceof
pet instanceof Dog
Check object's actual type
protected String name;
Accessible by subclasses
🎉 Lesson Complete!
You now understand inheritance, method overriding, polymorphism, the super keyword, and when to choose inheritance vs composition! These concepts are the backbone of extensible, maintainable Java applications.
Next up: Interfaces & Abstract Classes — define contracts and partial blueprints for maximum design flexibility.
Practice quiz
Which keyword makes a class inherit from a parent class?
- implements
- inherits
- extends
- super
Answer: extends. 'class Dog extends Animal' makes Dog inherit Animal's fields and methods.
Inside a child constructor, where must super(...) appear if the parent has no default constructor?
- As the very first statement
- Anywhere in the body
- As the last statement
- It is optional
Answer: As the very first statement. super(...) must be the first statement, or the code will not compile.
What is the main purpose of the @Override annotation?
- It makes a method run faster
- It hides the parent method
- It creates a new method
- It tells the compiler to verify the parent actually has this method
Answer: It tells the compiler to verify the parent actually has this method. @Override catches typos like speek() at compile time by checking the parent has the method.
Given Animal a = new Dog(); and Dog overrides speak(), what does a.speak() call?
- Animal's version
- Dog's version
- A compile error
- Neither — it returns null
Answer: Dog's version. Polymorphism: the actual object's (Dog's) overriding method runs.
What does 'pet instanceof Dog' evaluate to when pet refers to a Dog object?
- true
- false
- null
- A compile error
Answer: true. instanceof checks the object's runtime type; a Dog is a Dog, so it is true.
Which relationship is the correct test for using inheritance?
- 'has-a'
- 'uses-a'
- 'is-a'
- 'needs-a'
Answer: 'is-a'. Inheritance models 'is-a' (Dog is an Animal); composition models 'has-a'.
Which access modifier lets subclasses access a member but blocks unrelated outside classes?
- private
- protected
- public
- (default) package-private
Answer: protected. protected is accessible in the same class and subclasses, but not arbitrary other classes.
What does super.speak() do inside an overriding speak() method?
- Calls the child's speak() again
- Throws an error
- Skips the method
- Calls the parent class's version of speak()
Answer: Calls the parent class's version of speak(). super.method() invokes the parent class's implementation.
A private field in a parent class is accessible from a subclass.
- True
- False
- Only if marked @Override
- Only via instanceof
Answer: False. The table shows private members are NOT accessible from subclasses.
Why is favoring composition over deep inheritance recommended?
- Inheritance is always slower
- Composition removes the need for classes
- Deep hierarchies (5+ levels) become rigid and hard to change
- It is required by the compiler
Answer: Deep hierarchies (5+ levels) become rigid and hard to change. Deep inheritance hierarchies become rigid; the lesson advises 1-2 levels plus interfaces.
Continue this course
- Previous: Previous Lesson
- Next: Next Lesson