🌱BeginnerObject-Oriented Programming

Abstract Classes

Define incomplete class templates with abstract methods that subclasses must implement, enforcing a shared contract.

Overview

An abstract class is a class that cannot be instantiated on its own - it exists only to be extended. It can contain regular (concrete) methods with implementations AND abstract methods that have no body and must be implemented by every concrete subclass. Use abstract classes when you have shared behaviour to put in the parent, but part of the behaviour varies by subclass and the parent cannot provide a meaningful default. Example: Shape is a good abstract class. All shapes have a colour and a name, but you can't meaningfully compute area() for "a generic shape" - each concrete shape (Circle, Square) must provide its own area().

Key Points

  • •abstract class cannot be instantiated with new
  • •abstract method has no body: `abstract double area();`
  • •Every non-abstract subclass must implement all abstract methods
  • •Abstract classes can have fields, constructors, and concrete methods
  • •A subclass that does not implement all abstract methods must itself be abstract
  • •Abstract classes are used for "template" designs where some steps are fixed and others vary
  • •Use abstract classes for "is-a" with shared state/behaviour; use interfaces for capabilities (J039)

Examples

Abstract Shape class

abstract class Shape {
    String colour;

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

    // Abstract - subclasses must provide implementation
    abstract double area();
    abstract double perimeter();

    // Concrete - shared by all subclasses
    void describe() {
        System.out.printf("%s %s: area=%.2f, perimeter=%.2f%n",
            colour, getClass().getSimpleName(), area(), perimeter());
    }
}

class Circle extends Shape {
    double radius;

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

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

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

class Rectangle extends Shape {
    int width, height;

    Rectangle(String colour, int width, int height) {
        super(colour);
        this.width = width;
        this.height = height;
    }

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

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

public class Main {
    public static void main(String[] args) {
        Shape[] shapes = {
            new Circle("red", 5),
            new Rectangle("blue", 4, 6)
        };

        for (Shape s : shapes) {
            s.describe();
        }

        // new Shape("green"); // compile error - abstract class can't be instantiated
    }
}

describe() calls area() and perimeter() - those calls go to the appropriate subclass implementation at runtime.

Abstract class with template method pattern

abstract class DataProcessor {
    // Template method - defines the algorithm skeleton
    final void process() {
        readData();
        processData();
        writeResults();
    }

    abstract void readData();
    abstract void processData();

    // Concrete with default - subclasses can override if needed
    void writeResults() {
        System.out.println("Writing results to default output...");
    }
}

class CsvProcessor extends DataProcessor {
    @Override
    void readData() { System.out.println("Reading CSV file..."); }

    @Override
    void processData() { System.out.println("Processing CSV rows..."); }
}

class JsonProcessor extends DataProcessor {
    @Override
    void readData() { System.out.println("Parsing JSON..."); }

    @Override
    void processData() { System.out.println("Processing JSON nodes..."); }

    @Override
    void writeResults() { System.out.println("Saving to JSON output..."); }
}

public class Main {
    public static void main(String[] args) {
        new CsvProcessor().process();
        System.out.println("---");
        new JsonProcessor().process();
    }
}

process() is final - subclasses can't change the overall algorithm, only the individual steps.

Common Mistakes

  • Trying to instantiate an abstract class: `new Shape("red")` - compile error
  • Forgetting to implement all abstract methods in a subclass - the subclass must also be declared abstract
  • Mixing up abstract classes (partial implementation, can have fields) and interfaces (no fields, pure contract)

Note

Choose abstract class over interface when subclasses share state (fields) or common concrete method implementations. If you only need to define a contract with no shared state, use an interface (J039).

Exercises

Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.

Loading editor…

Completed: 0 / 4 exercises

Knowledge Check

Question 1 of 4

What does it mean for a class to be abstract?