Matheion

MA-JAVA0 · Module 16

Abstract classes and interface design

Topics: abstract · Concrete partial impl · implements · Multiple interfaces

Overview

Session 16 — Abstract classes and interface design

Some abstractions are pure capability (interfaces); others are partially built machines that share real fields and helper methods but refuse to be instantiated until a concrete subtype finishes the job (abstract classes). This session compares abstract methods and classes with interface / implements, when to combine them, and why Java allows multiple interfaces but only one superclass.

Time: about 90 minutes.

Concepts

1. Abstract classes cannot be constructed

Mark a class abstract when it models a family but is incomplete on its own:

public abstract class Task {
    private final String id;

    protected Task(String id) {
        this.id = id;
    }

    public String id() {
        return id;
    }

    public abstract int estimateMinutes();
}

new Task("x") is illegal. Concrete subclasses must implement every inherited abstract method (unless they stay abstract too).


2. Abstract methods

An abstract method has a signature and no body—subclasses supply behaviour:

public abstract int estimateMinutes();

If any abstract method remains unimplemented in a concrete class, the compiler rejects the file.


3. Interfaces as pure contracts

An interface historically held only abstract instance methods (plus constants). Modern Java adds default and static methods on interfaces, blurring the line slightly, but the mental model remains: an interface names what callers may do without dictating how unless defaults suffice.

public interface Schedulable {
    int estimateMinutes();
}

A class implements Schedulable, Runnable may satisfy several contracts—something inheritance cannot duplicate with extends.


4. When to choose which

  • Interface — cross-cutting capability (Comparable, Runnable, Schedulable). Multiple inheritance of type is allowed.
  • Abstract class — shared non-trivial state, protected helpers, template methods that call abstract hooks:
public final void runSafely() {
    prepare();
    executeCore(); // abstract hook
    cleanup();
}

protected abstract void executeCore();

5. implements with extends

Order in source: extends first, then implements:

public class BackupJob extends Task implements Runnable {
    @Override
    public int estimateMinutes() {
        return 30;
    }

    @Override
    public void run() {
        System.out.println("starting backup " + id());
    }
}

BackupJob is a concrete Task and also satisfies Runnable for thread pools or executors.


6. Pitfalls

  • Declaring abstract on a private method—illegal; abstract members must be visible to subclasses.
  • Forgetting that abstract classes still need constructors for their fields even if you never new the abstract type directly.
  • Fat interfaces with dozens of methods—split into smaller composable interfaces when possible.

Examples

Example A — abstract template

public abstract class CsvExportJob {
    protected abstract String[] header();

    protected abstract Iterable<String[]> rows();

    public final void write(java.io.PrintWriter out) {
        out.println(String.join(",", header()));
        for (String[] row : rows()) {
            out.println(String.join(",", row));
        }
    }
}

Subclasses supply header and rows; the superclass owns the write algorithm—classic template method without duplicating CSV joining in every subtype.


Example B — interface for pluggable policy

public interface Pricing {
    int centsFor(int quantity);
}

public class BulkPricing implements Pricing {
    private final int unitCents;
    private final int threshold;
    private final int discountPercent;

    public BulkPricing(int unitCents, int threshold, int discountPercent) {
        this.unitCents = unitCents;
        this.threshold = threshold;
        this.discountPercent = discountPercent;
    }

    @Override
    public int centsFor(int quantity) {
        int gross = unitCents * quantity;
        if (quantity >= threshold) {
            return gross * (100 - discountPercent) / 100;
        }
        return gross;
    }
}

Callers depend on Pricing, not this concrete class—tests can swap a stub implementation.

Bridge

Abstract types in API design

  1. Java standard library mixes both: InputStream is abstract; Comparable<T> is an interface.

  2. GUI and game engines often expose abstract base scenes or controllers with hooks (onStart, onTick) filled by your subclasses.

  3. Dependency injection prefers interfaces as injection keys; abstract classes appear when frameworks ship optional base classes with lots of wiring.

  4. Testing doubles almost always implement interfaces rather than extend production classes—fewer surprises from hidden superclass state.

If you can articulate why CsvExportJob stayed abstract while Pricing is an interface, you are choosing abstractions deliberately.


Your turn (informal)

Pick a tiny feature from software you use daily. Would you model its extension point as an interface (capability only) or an abstract class (shared code + hooks)? Two sentences defending the choice without naming the product’s trademarks excessively.

Practice check — abstract classes and interfaces

Multiple-choice check from the Matheion app lesson. Pick an answer for each question, then submit.

  1. 1.An `abstract` class…

  2. 2.A class can `implements`…

  3. 3.When might you choose an abstract class over an interface?

  4. 4.Interface methods (classic style) are…

  5. 5.Can an abstract class have constructors?