Enumerated types: type-safe constants
Java enums as full classes — constructors, fields, methods, values(), switch, and the EnumSet/EnumMap companions.
Beyond int constants
Before Java 5 you faked a set of named constants with public static final int:
public static final int STATE_OK = 0;
public static final int STATE_WARN = 1;
public static final int STATE_FAIL = 2;
This is the *int enum pattern*, and it is unsafe. There is nothing stopping you from passing 99 where a state is expected, and a method that prints one just shows 1, not WARN. An enum fixes all of this: it declares a fixed set of named instances, and the compiler treats them as a distinct type.
public enum State { OK, WARN, FAIL }
State s = State.WARN;
// State s = 1; // compile error — int is not a State
System.out.println(s); // prints WARN (the constant's name)
Each constant is a public static final instance of the enum type, created once by the JVM. Because there is exactly one object per constant, you compare them with == (no .equals() needed), and switch lets you name the constants directly.
An enum is a full class
The key idea the exam tests: an enum can have fields, a constructor, and methods, just like any class. The constructor is implicitly private (you can never call new State()), and you pass its arguments in the constant list:
public enum AlarmLevel {
GREEN(0, "all clear"),
AMBER(1, "investigate"),
RED(2, "shut down now");
private final int severity;
private final String advice;
AlarmLevel(int severity, String advice) {
this.severity = severity;
this.advice = advice;
}
public int severity() { return severity; }
public String advice() { return advice; }
public boolean isCritical() { return severity >= 2; }
}
Using it reads cleanly and stays type-safe:
AlarmLevel a = AlarmLevel.RED;
System.out.println(a.advice()); // shut down now
System.out.println(a.isCritical()); // true
System.out.println(a.severity()); // 2
Built-in members every enum has
The compiler synthesises these automatically (you do not write them):
- values() — a static method returning an array of all constants in declaration order. Great for iterating.
- valueOf(String) — static; returns the constant whose name matches exactly, or throws IllegalArgumentException.
- name() — the constant's identifier as a String ("RED").
- ordinal() — its zero-based position in the declaration (RED.ordinal() is 2).
for (AlarmLevel level : AlarmLevel.values()) {
System.out.println(level.ordinal() + ": " + level.name() + " -> " + level.advice());
}
// 0: GREEN -> all clear
// 1: AMBER -> investigate
// 2: RED -> shut down now
switch on an enum
Inside a switch, you use the bare constant name — case RED:, never case AlarmLevel.RED: (the latter is a compile error). The compiler knows the type from the selector:
String plan;
switch (a) {
case GREEN: plan = "continue"; break;
case AMBER: plan = "watch"; break;
case RED: plan = "shutdown"; break;
default: plan = "unknown";
}
EnumSet and EnumMap
When the keys or members of a collection are enum constants, use the specialised containers EnumSet and EnumMap (from java.util). Internally they are backed by a bit-vector / array indexed by ordinal(), so they are far faster and smaller than HashSet/HashMap and they iterate in declaration order.
EnumSet<AlarmLevel> actionable = EnumSet.of(AlarmLevel.AMBER, AlarmLevel.RED);
boolean act = actionable.contains(a); // true for RED
EnumMap<AlarmLevel, String> colors = new EnumMap<>(AlarmLevel.class);
colors.put(AlarmLevel.RED, "#ff0000");