Learn Java in 10 DaysDay 7: Inheritance and Polymorphism
books.chapter 7Learn Java in 10 Days

Day 7: Inheritance and Polymorphism

What You'll Learn Today

  • Inheritance (extends)
  • Method overriding
  • Abstract classes
  • Interfaces
  • Polymorphism
  • Sealed classes (Java 17+)

Inheritance

Inheritance lets you create a new class by extending an existing one.

// Parent class (superclass)
public class Animal {
    protected String name;

    Animal(String name) {
        this.name = name;
    }

    void speak() {
        System.out.println(name + ": ...");
    }
}

// Child class (subclass)
public class Dog extends Animal {
    private String breed;

    Dog(String name, String breed) {
        super(name); // call the parent constructor
        this.breed = breed;
    }

    @Override
    void speak() {
        System.out.println(name + ": Woof!");
    }

    void fetch() {
        System.out.println(name + " fetched the ball!");
    }
}
flowchart TB
    Animal["Animal<br>name, speak()"]
    Dog["Dog<br>breed, fetch()"]
    Cat["Cat<br>indoor"]
    Animal --> Dog
    Animal --> Cat
    style Animal fill:#3b82f6,color:#fff
    style Dog fill:#22c55e,color:#fff
    style Cat fill:#22c55e,color:#fff
Term Description
extends Inherit from a class
super Reference to the parent class
@Override Override a method
protected Accessible from subclasses

Constraint: Java supports single inheritance only. A class can extends just one other class.


Method Overriding

public class Cat extends Animal {
    Cat(String name) {
        super(name);
    }

    @Override
    void speak() {
        System.out.println(name + ": Meow!");
    }
}

// Usage
Animal dog = new Dog("Buddy", "Shiba");
Animal cat = new Cat("Whiskers");

dog.speak(); // Buddy: Woof!
cat.speak(); // Whiskers: Meow!

Rules for Overriding

Rule Requirement
Method name Must be the same
Parameters Must be the same
Return type Same or a subtype
Access modifier Same or broader
@Override Recommended

Abstract Classes

An abstract class cannot be instantiated directly. It forces subclasses to provide specific implementations.

abstract class Shape {
    String color;

    Shape(String color) {
        this.color = color;
    }

    // Abstract methods (no implementation)
    abstract double area();
    abstract double perimeter();

    // Concrete method (has implementation)
    void display() {
        System.out.printf("%s: area=%.2f, perimeter=%.2f%n",
            color, area(), perimeter());
    }
}

class Circle extends Shape {
    double radius;

    Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * radius * radius;
    }

    @Override
    double perimeter() {
        return 2 * Math.PI * radius;
    }
}

class Rectangle extends Shape {
    double width, height;

    Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }

    @Override
    double area() {
        return width * height;
    }

    @Override
    double perimeter() {
        return 2 * (width + height);
    }
}

When to use: Abstract classes are ideal when subclasses share common state (fields).


Interfaces

An interface defines a contract that classes must fulfill.

interface Printable {
    void print(); // abstract method
}

interface Savable {
    void save(String filename);

    // Default method (Java 8+)
    default void autoSave() {
        save("auto_save.dat");
    }
}

// Implementing multiple interfaces
class Document implements Printable, Savable {
    private String content;

    Document(String content) {
        this.content = content;
    }

    @Override
    public void print() {
        System.out.println("Printing: " + content);
    }

    @Override
    public void save(String filename) {
        System.out.println("Saving to " + filename + ": " + content);
    }
}
flowchart TB
    Printable["<<interface>><br>Printable<br>print()"]
    Savable["<<interface>><br>Savable<br>save(), autoSave()"]
    Document["Document<br>content"]
    Printable --> Document
    Savable --> Document
    style Printable fill:#8b5cf6,color:#fff
    style Savable fill:#8b5cf6,color:#fff
    style Document fill:#22c55e,color:#fff

Abstract Class vs Interface

Feature Abstract Class Interface
Inheritance Single only Multiple allowed
Fields Yes Constants only
Constructors Yes No
Default methods Yes Yes (Java 8+)
Use case Shared state and behavior Defining contracts

Polymorphism

Polymorphism allows different implementations to be called through the same type.

Shape[] shapes = {
    new Circle("Red", 5),
    new Rectangle("Blue", 4, 6),
    new Circle("Green", 3)
};

for (Shape shape : shapes) {
    shape.display(); // calls the appropriate subclass method
}

instanceof and Pattern Matching (Java 16+)

// Traditional approach
if (shape instanceof Circle) {
    Circle c = (Circle) shape;
    System.out.println("Radius: " + c.radius);
}

// Pattern matching (Java 16+)
if (shape instanceof Circle c) {
    System.out.println("Radius: " + c.radius);
}

// Pattern matching in switch expressions (Java 21+)
String info = switch (shape) {
    case Circle c -> "Circle (radius: " + c.radius + ")";
    case Rectangle r -> "Rectangle (" + r.width + " x " + r.height + ")";
    default -> "Unknown shape";
};

Sealed Classes (Java 17+)

Sealed classes restrict which classes can extend them.

sealed abstract class Payment permits CreditCard, BankTransfer, Cash {
    abstract double amount();
}

final class CreditCard extends Payment {
    private double amount;
    private String cardNumber;

    CreditCard(double amount, String cardNumber) {
        this.amount = amount;
        this.cardNumber = cardNumber;
    }

    @Override
    double amount() { return amount; }
}

final class BankTransfer extends Payment {
    private double amount;

    BankTransfer(double amount) {
        this.amount = amount;
    }

    @Override
    double amount() { return amount; }
}

final class Cash extends Payment {
    private double amount;

    Cash(double amount) {
        this.amount = amount;
    }

    @Override
    double amount() { return amount; }
}
Modifier Description
sealed Only permitted subclasses can inherit
permits Lists the allowed subclasses
final No further inheritance allowed
non-sealed Removes the restriction

Hands-On: Zoo Simulation

interface Feedable {
    String favoriteFood();
}

abstract class Animal {
    protected String name;
    protected int age;

    Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    abstract String speak();

    @Override
    public String toString() {
        return String.format("%s (%d years old)", name, age);
    }
}

class Dog extends Animal implements Feedable {
    Dog(String name, int age) { super(name, age); }

    @Override
    String speak() { return "Woof!"; }

    @Override
    public String favoriteFood() { return "Bones"; }
}

class Cat extends Animal implements Feedable {
    Cat(String name, int age) { super(name, age); }

    @Override
    String speak() { return "Meow!"; }

    @Override
    public String favoriteFood() { return "Fish"; }
}

class Parrot extends Animal implements Feedable {
    private String phrase;

    Parrot(String name, int age, String phrase) {
        super(name, age);
        this.phrase = phrase;
    }

    @Override
    String speak() { return phrase; }

    @Override
    public String favoriteFood() { return "Seeds"; }
}

public class Zoo {
    public static void main(String[] args) {
        Animal[] animals = {
            new Dog("Buddy", 3),
            new Cat("Whiskers", 5),
            new Parrot("Polly", 2, "Hello there!")
        };

        System.out.println("=== Zoo ===");
        for (Animal animal : animals) {
            System.out.printf("%s -> %s", animal, animal.speak());
            if (animal instanceof Feedable f) {
                System.out.printf(" [Favorite food: %s]", f.favoriteFood());
            }
            System.out.println();
        }
    }
}

Summary

Concept Description
Inheritance Extend an existing class with extends
super Call parent class constructors and methods
@Override Override a method from the parent
Abstract class Cannot be instantiated; forces subclass implementation
Interface A contract that supports multiple implementation
Polymorphism Same type, different behavior
sealed Restricts inheritance (Java 17+)
Pattern matching Concise instanceof checks (Java 16+)

Key Takeaways

  1. Java supports single inheritance (extends only one class)
  2. Interfaces allow multiple implementation
  3. Polymorphism makes your code flexible and extensible
  4. sealed classes provide safe, controlled inheritance

Exercises

Exercise 1: Basic

Create a Vehicle superclass, then extend it with Car and Bicycle. Override toString() in each subclass.

Exercise 2: Applied

Create a Student class that implements the Comparable interface so that students can be sorted by their grades.

Challenge

Using sealed classes, implement an area calculator with pattern matching (switch expressions). Support Circle, Rectangle, and Triangle, and return the area for each.


References


Next up: In Day 8, you'll learn about Exception Handling and File I/O -- mastering how Java deals with errors and file operations.