Skip to main content

Section 8.5 Basic Inheritance in Java

Subsection 8.5.1 Why Do We Need Inheritance?

As you saw before, repeating similar fields or methods in multiple classes can create a maintenance headache. Inheritance addresses this by letting one class "share" its common code with other classes. In Java, the class that provides shared code is called the superclass, while the classes that reuse this code are called subclasses.
A subclass automatically inherits all non-private fields and methods from its superclass. This inheritance enables you to:
  • Reuse existing code without copying it
  • Add new fields and methods to extend functionality
  • Override inherited methods to provide specialized behavior
  • Leverage polymorphism to write more flexible code
Imagine a game where Entity represents anything that can appear in the world. Player and Monster might both need a health field and a takeDamage method. Rather than copy-pasting them into each class, we can define these once in Entity. Subclasses like Player or Monster then inherit the same health and takeDamage logic, but are still free to add unique attributes or special behaviors.
Perhaps the most powerful benefit of inheritance is polymorphism – the ability to treat objects of different subclasses as instances of their common superclass. This enables you to write code that operates on the general type while automatically getting the specialized behavior of specific subtypes:
// Using polymorphism
// Collection holding different entity types
List<Entity> entities = new ArrayList<>();
entities.add(new Player("Hero", 100));
entities.add(new Monster("Goblin", 50));
entities.add(new NPC("Shopkeeper", 30));

// Apply damage to all entities
// Each will use its own version of takeDamage()
for (Entity e : entities) {
    e.takeDamage(10);  // Polymorphic call
}
In this example, the same takeDamage() call works differently depending on the actual type of entity, even though our code only deals with the general Entity type. This is a powerful mechanism that enables flexible and extensible designs.

Subsection 8.5.2 Subclasses and Superclasses

The superclass is your general or "parent" class. Subclasses (sometimes called "child" classes) extend this superclass and become more specialized. This relationship is often described as "is-a": a Monster is-a(n) Entity. This helps you follow the DRY principle because you only write the shared logic once.
Here are some examples of superclass-subclass relationships organized by domain:
Domain Superclass Subclasses What’s Inherited
Game Development Entity Player, Monster, NPC position, health, collision detection
Financial Systems Account CheckingAccount, SavingsAccount balance, deposit/withdraw methods
Graphics Shape Circle, Rectangle, Triangle position, color, draw methods
Here’s a visual representation of a simple class hierarchy:
Entity
 ├── Player
 ├── Monster
 └── NPC
This diagram shows Entity as the superclass with three subclasses: Player, Monster, and NPC. Each of these subclasses inherits properties and behaviors from Entity, while potentially adding their own specialized features.
In this hierarchy, the Entity class would contain common attributes like position, health, and basic movement functionality. Each subclass then adds specialized behaviors:
  • Player adds user input handling, inventory management, and experience/leveling
  • Monster adds AI pathfinding, aggression logic, and combat behaviors
  • NPC adds dialogue systems, quest management, and interaction options
This structure allows you to write code that works with any Entity while still leveraging specialized behaviors when needed.

Subsection 8.5.3 Common Inheritance Mistakes

Note 8.5.1. Inheritance Anti-Patterns.

The most common inheritance mistake is using it for "has-a" relationships instead of "is-a" relationships.
  • Incorrect: Car extends Engine (A car has an engine, it is not a type of engine)
  • Correct: Car contains an Engine field (composition)
Other common anti-patterns include:
  • Deep inheritance hierarchies: More than 2-3 levels deep creates fragile, difficult-to-maintain code where changes to base classes can have unexpected effects rippling throughout the hierarchy
  • Inheritance for code reuse only: Using inheritance when there’s no logical "is-a" relationship leads to confusing designs that violate the principle of least surprise
  • Inheriting from final classes or those not designed for extension: This limits future flexibility and may violate assumptions made by the original class designer
Here’s an example of incorrect use of inheritance:
// Incorrect: Using inheritance inappropriately
public class Car extends Engine {
    private String model;
    
    // Car functionality...
}
This design is flawed because a Car is not a specialized type of Engine—it contains an engine as one of its components. The consequences of this flawed design include:
  • A car would inherit all engine methods like ignite() and increaseFuelFlow() at the top level, creating a confusing API
  • Changes to the Engine class would directly impact Car, creating tight coupling
  • You couldn’t change the engine in a car without changing the car itself
Here’s how it should be corrected using composition:
// Correct: Using composition for a "has-a" relationship
public class Engine {
    private int horsepower;
    
    public void start() {
        // Engine starting logic
    }
}

public class Car {
    private Engine engine;  // Composition
    private String model;
    
    public Car(Engine engine, String model) {
        this.engine = engine;
        this.model = model;
    }
    
    public void startCar() {
        engine.start();
        // Additional car startup logic
    }
}
With composition, the relationship is clearer: a Car has an Engine, but it is not an Engine. This design also makes it easier to change or replace the engine without affecting the car’s implementation.
Check Your Understanding

Checkpoint 8.5.2.

What is the primary benefit of using inheritance in Java?
  • It allows multiple classes to have the same name.
  • It prevents subclasses from modifying superclass behavior.
  • It enables code reuse by allowing subclasses to inherit common fields and methods.
  • It automatically generates code for all subclasses.

Checkpoint 8.5.3.

Which of the following statements about Java inheritance is true?
  • A subclass must always override all methods from its superclass.
  • A subclass can inherit private fields and methods directly.
  • A subclass automatically inherits all non-private fields and methods.
  • A subclass cannot have additional fields or methods beyond what is inherited.

Checkpoint 8.5.4.

How does inheritance help reduce code duplication?
  • By requiring each class to define its own fields and methods separately.
  • By allowing a superclass to share common code with multiple subclasses.
  • By automatically copying code from the superclass into each subclass.
  • By making all methods static so they can be used anywhere.

Checkpoint 8.5.5.

How does polymorphism enhance flexibility when using inheritance?
  • It allows an object of a subclass to be treated as an instance of its superclass.
  • It forces all subclasses to override superclass methods.
  • It prevents method overriding in subclasses.
  • It removes the need for constructors in subclasses.
You have attempted of activities on this page.