Inner classes & control frameworks
Why inner classes power the Event/Controller design; static nested classes.
The control-framework pattern
A control framework separates the *generic machinery* (run events when they are ready) from the *specific responses* (what each event does). The Greenhouse project is the textbook example, and inner classes are the glue:
- Event (abstract) defines ready() + abstract action().
- Controller holds a List<Event> and runs the loop — it knows nothing about lights or water.
- GreenhouseControls extends Controller defines each response (LightOn, WaterOn, Bell, …) as an inner class, so each action() can directly touch the greenhouse's private state.
Inner classes are the right tool here precisely because each event needs to reach back into the enclosing GreenhouseControls object's fields (light, water, thermostat) — which is exactly what an inner class can do.
Worked example — Controller.run()
public class Controller {
private List<Event> eventList = new ArrayList<Event>();
public void addEvent(Event c) { eventList.add(c); }
public void run() {
while (eventList.size() > 0)
// copy so we don't modify the list while iterating it:
for (Event e : new ArrayList<Event>(eventList))
if (e.ready()) {
System.out.println(e); // calls e.toString()
e.action(); // polymorphic dispatch
eventList.remove(e);
}
}
}
The loop is pure framework: it never names a single event type. e.action() is a polymorphic call — at run time it invokes the inner class's overriding action(), so a LightOn flips the light while a WaterOn opens the water, all through one Event reference. Adding a brand-new event type requires zero changes to Controller. (The defensive copy new ArrayList<Event>(eventList) lets action() add or remove events without corrupting the iteration — a ConcurrentModificationException guard you will revisit with collections.)
Static nested classes
If a nested class does not need a link to an outer instance, declare it static — a static nested class. It is a plain class that merely lives inside another for namespacing; it has no enclosing this, cannot see outer instance fields, and is created without an outer instance:
class Holder {
static class Pair { // static nested — no outer link
final int a, b;
Pair(int a, int b) { this.a = a; this.b = b; }
}
}
Holder.Pair p = new Holder.Pair(1, 2); // no Holder instance needed
Use a non-static inner class when it must reach enclosing state (the events); use a static nested class for a self-contained helper that is merely scoped to its owner (a data pair, a builder).