Memra

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 namecase 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");
NORMAL ~/memra/learn/comp-308/enumerated-types utf-8 LF