Abstract classes & methods
Partially-implemented base classes that define a contract subclasses must complete.
A base class that cannot be instantiated
Sometimes a base class exists only to define a common shape for its subclasses — it has no meaningful standalone object. In the Greenhouse control system, every control action (turn the light on, open the water, ring the bell) *is an* Event, but there is no such thing as a bare "Event" object — the *what to do* differs for each kind. Java models this with an abstract class.
An abstract class:
- is declared abstract and cannot be instantiated — new Event(1000) is a compile error;
- may contain ordinary fields, constructors, and fully-implemented methods (shared behaviour);
- may contain abstract methods — a method signature with no body, ending in a semicolon, that every concrete subclass is forced to override.
If a class has even one abstract method it MUST be declared abstract. The compiler then guarantees that any *concrete* (non-abstract) subclass supplies a body for every inherited abstract method, so callers can rely on the method existing.
Worked example — the project's Event
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
public void start() { // shared, concrete
eventTime = System.currentTimeMillis() + delayTime;
}
public boolean ready() { // shared, concrete
return System.currentTimeMillis() >= eventTime;
}
public abstract void action(); // abstract — no body
}
Notice the mix: Event carries real state (eventTime, delayTime), a constructor, and two concrete methods (start, ready) that every event shares — but action() is abstract because *what the event does* is different for each subclass. A subclass becomes concrete only by implementing action():
class Bell extends Event {
public Bell(long delayTime) { super(delayTime); }
public void action() { /* ring */ } // now concrete
}
Because Event has an abstract method, you can declare a variable of type Event and hold any subclass through it (Event e = new Bell(1000);) — but you can never write new Event(...).