Abstract Classes and Methods

intermediate25 min

A class that is deliberately incomplete

Sometimes a base class knows that something must happen but not how. Every subsystem needs a periodic update, but what “update” means depends entirely on which subsystem it is.

You could give the parent an empty method and hope subclasses remember to override it:

class Subsystem {
    void periodic() { }         // does nothing — easy to forget to override
}

That compiles, and a subclass that forgets silently does nothing at all. An abstract method makes it a compile error instead:

abstract class Subsystem {
    protected final String name;

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

    abstract void periodic();       // no body — subclasses must supply one

    void describe() {               // ordinary method — inherited as usual
        System.out.println("subsystem: " + name);
    }
}

An abstract method declares a signature and ends with a semicolon instead of a body. Any concrete subclass must implement it:

class Intake extends Subsystem {
    Intake() { super("intake"); }

    @Override
    void periodic() {
        System.out.println("checking intake sensor");
    }
}

Omit periodic in Intake and the code will not compile. The requirement is enforced rather than hoped for.

Abstract classes cannot be instantiated

Subsystem s = new Subsystem("thing");   // compile error
Subsystem s = new Intake();             // fine

This is the other half of the point. A Subsystem with no periodic is not a usable object, so Java refuses to create one. You can still use the type as a variable, parameter, or return type — you just cannot construct it directly.

The two effects of `abstract`

On a method: no body, and every concrete subclass must provide one.

On a class: cannot be instantiated. Required if the class has any abstract method, but also usable on its own when you simply want to prevent direct construction.

A class with an abstract method must be declared abstract. The reverse is not true — an abstract class may have no abstract methods at all.

Mixing abstract and concrete

This is what abstract classes offer that interfaces historically did not: real fields, constructors, and finished methods alongside the abstract ones.

abstract class Subsystem {
    protected final String name;
    private boolean enabled = false;

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

    // concrete — shared by every subclass
    void enable()  { enabled = true; }
    void disable() { enabled = false; }
    boolean isEnabled() { return enabled; }

    // abstract — each subclass decides
    abstract void periodic();

    // concrete, but built on the abstract part
    final void update() {
        if (enabled) {
            periodic();
        }
    }
}

update is interesting: it is finished code in the parent that calls a method the parent has not written. The parent controls when the work happens; the subclass controls what the work is.

Marking update as final means subclasses cannot change that ordering — they can only fill in periodic. That combination, a fixed skeleton with subclass-supplied steps, is a common and useful shape.

Partial implementations

An abstract class may extend another abstract class and implement only some of the inherited abstract methods, leaving the rest for further subclasses:

abstract class Subsystem {
    abstract void periodic();
    abstract void stop();
}

abstract class MotorSubsystem extends Subsystem {
    protected final Motor motor;

    MotorSubsystem(Motor motor) { this.motor = motor; }

    @Override
    void stop() {                    // supplied here for all motor subsystems
        motor.set(0);
    }
    // periodic() left abstract — still each subclass's job
}

class Intake extends MotorSubsystem {
    Intake(Motor motor) { super(motor); }

    @Override
    void periodic() { motor.set(0.6); }
}

MotorSubsystem is still abstract because periodic remains unimplemented — but it has removed duplication for everything below it.

Abstract class or interface

Both let you write code against a type rather than a specific class. The distinction has narrowed since interfaces gained default methods, but real differences remain.

Abstract classInterface
How many can a class have?OneMany
Instance fieldsYesNo — constants only
ConstructorsYesNo
Method bodiesYesYes, via default
Private / protected membersYesMostly public
Expressesis a kind ofis capable of

Choosing between them

Use an abstract class when subclasses share state — fields, a constructor, or partially-finished behaviour that operates on those fields.

Use an interface when you are describing a capability that unrelated classes might have, or when a class needs to fill several roles at once.

When both would work, prefer the interface. Single inheritance is a scarce resource: a class can implement many interfaces but extend only one class, so spending that slot commits the design in a way an interface does not.

A template method

The update/periodic split above has a name — the template method pattern. The parent defines the sequence; subclasses fill in steps.

abstract class AutoRoutine {
    // fixed sequence, subclasses cannot reorder it
    final void run() {
        initialise();
        while (!isFinished()) {
            step();
        }
        cleanUp();
    }

    void initialise() { }             // optional hook, default does nothing
    abstract void step();             // required
    abstract boolean isFinished();    // required
    void cleanUp() { }                // optional hook
}

Distinguishing required steps (abstract) from optional hooks (concrete, empty) is deliberate. A subclass must supply step and isFinished; it may ignore initialise and cleanUp.

Common mistakes

  • A body on an abstract method. It ends with a semicolon, not { }.
  • An abstract method in a non-abstract class. The class must be abstract too.
  • Trying to instantiate an abstract class.
  • Forgetting a subclass is still abstract if it leaves any abstract method unimplemented.
  • private abstract — contradictory, since a private method cannot be overridden. It does not compile.
  • Calling an abstract method from the constructor. The subclass’s version runs before its fields are initialised.
  • Using an abstract class where an interface would do, spending the one inheritance slot for nothing.

Practice

  1. Write an abstract Shape class with an abstract area() method and a concrete describe() that prints the area. Add Circle and Rectangle.
  2. Try to instantiate Shape directly and read the compile error.
  3. Write a subclass that forgets to implement area() and read that error too.
  4. Add an abstract MotorSubsystem between a Subsystem base and a concrete subclass, implementing only some methods at the middle level.
  5. Write a template method: a final method defining a fixed sequence, with one abstract step and one optional hook.
Hints
  1. describe() can call area() even though the parent never implements it.
  2. and 3. Both errors are worth seeing once so you recognise them later.
  3. The middle class stays abstract because something is still unimplemented.
  4. Empty concrete methods make good optional hooks.

Next

Related