IntermediateJava · Lesson 1 of 9

Inheritance, Interfaces & Polymorphism

extends, abstract classes, interfaces with default methods, and polymorphism.

Inheritance (extends) lets a class reuse and specialise another. An abstract class can't be instantiated and may declare abstract methods that subclasses must implement. Use super(...) to call the parent constructor and @Override to mark overridden methods.

An interface declares what a type can do, without saying how. A class can implement many interfaces. Interfaces can also provide default methods with a shared implementation.

Polymorphism: code that works with the interface or parent type (Notifier, StaffMember) works with every implementation. This is how you swap an SMS notifier for an email one — or for a fake in tests — without changing the calling code.

Staff.javaJava
import java.util.List;

public class Staff {
    public static void main(String[] args) {
        List<StaffMember> staff = List.of(
            new Teacher("Ms. Lyimo", 1_200_000, "Biology"),
            new Teacher("Mr. Mwakyusa", 1_250_000, "Mathematics"),
            new Bursar("Mrs. Mrema", 1_100_000)
        );

        long payroll = 0;
        for (StaffMember m : staff) {
            System.out.println(m.describe());            // each class's own version
            payroll += m.monthlyPay();
        }
        System.out.println("Monthly payroll: " + payroll);

        Notifier sms = new SmsNotifier();
        sms.notifyAll(List.of("0712345678", "0754000111"), "Staff meeting at 14:00");
    }
}

abstract class StaffMember {
    private final String name;
    protected final long salary;

    StaffMember(String name, long salary) {
        this.name = name;
        this.salary = salary;
    }

    String name() { return name; }

    abstract String role();

    long monthlyPay() { return salary; }

    String describe() {
        return name() + " - " + role() + " - " + monthlyPay() + " TSh";
    }
}

class Teacher extends StaffMember {
    private final String subject;

    Teacher(String name, long salary, String subject) {
        super(name, salary);
        this.subject = subject;
    }

    @Override
    String role() { return subject + " teacher"; }

    @Override
    long monthlyPay() { return salary + 100_000; }   // teaching allowance
}

class Bursar extends StaffMember {
    Bursar(String name, long salary) { super(name, salary); }

    @Override
    String role() { return "Bursar"; }
}

interface Notifier {
    void send(String to, String message);

    default void notifyAll(List<String> recipients, String message) {
        for (String r : recipients) send(r, message);
    }
}

class SmsNotifier implements Notifier {
    @Override
    public void send(String to, String message) {
        System.out.println("SMS to " + to + ": " + message);
    }
}

Key points

  • Abstract classes share code; interfaces define capabilities a class can promise.
  • Program against interfaces/parent types — implementations become swappable.
  • Always mark overrides with @Override so the compiler checks them.

Exercise

Create a Shape interface with area() and perimeter(), and Circle, Rectangle and Triangle classes. Store mixed shapes in a List<Shape>, print each one and the total area.

Show solution

Try the exercise yourself first — then compare your approach with this one.

The Shape interface promises area() and perimeter(), and gives every shape a shared describe() default method. The loop only knows about Shape — each object runs its own version of the methods (polymorphism).

Shapes.javaJava
import java.util.List;

public class Shapes {
    public static void main(String[] args) {
        List<Shape> shapes = List.of(new Circle(1), new Rectangle(3, 4), new Triangle(3, 4, 5));
        double total = 0;
        for (Shape s : shapes) {
            System.out.println(s.describe());
            total += s.area();
        }
        System.out.printf("Total area: %.2f%n", total);   // Total area: 21.14
    }
}

interface Shape {
    double area();
    double perimeter();

    default String describe() {
        return String.format("%s: area %.2f, perimeter %.2f", getClass().getSimpleName(), area(), perimeter());
    }
}

class Circle implements Shape {
    private final double r;
    Circle(double r) { this.r = r; }
    public double area() { return Math.PI * r * r; }
    public double perimeter() { return 2 * Math.PI * r; }
}

class Rectangle implements Shape {
    private final double w, h;
    Rectangle(double w, double h) { this.w = w; this.h = h; }
    public double area() { return w * h; }
    public double perimeter() { return 2 * (w + h); }
}

class Triangle implements Shape {
    private final double a, b, c;
    Triangle(double a, double b, double c) { this.a = a; this.b = b; this.c = c; }
    public double perimeter() { return a + b + c; }
    public double area() {                     // Heron's formula
        double s = perimeter() / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }
}

Check your understanding

  1. How many interfaces can a Java class implement?

  2. What is an abstract class?

  3. Why mark overriding methods with @Override?

  4. What is polymorphism?

Ask AI