Enum
In short: A dedicated type for a fixed, limited set of named constants — e.g. weekdays or status values, where only certain values should be valid.
In more detail: An enum is technically a special class, so it can also have its own fields, constructors, and methods. The big advantage over simple String or int constants: the compiler prevents invalid values, and a switch over an enum can even be checked by the compiler for completeness.
enum Status { OPEN, IN_PROGRESS, DONE }In Depth
enum Status {
OPEN("Waiting to be processed"),
IN_PROGRESS("Currently being processed"),
DONE("Completed");
private final String description; // every constant carries its own value
Status(String description) { // enum constructor, always private (implicitly)
this.description = description;
}
public String getDescription() {
return description;
}
}
Status s = Status.IN_PROGRESS;
System.out.println(s.getDescription());
switch (s) {
case OPEN -> System.out.println("Not started yet");
case IN_PROGRESS -> System.out.println("Running");
case DONE -> System.out.println("Finished");
// no default needed - compiler knows all 3 values are covered
}The decisive advantage over String constants ("OPEN", "IN_PROGRESS", …): typos like "OPN" are caught immediately at compile time with an enum, but only (if at all) at runtime with strings. Enums also offer built-in methods like values() (returns an array of all constants, handy for loops) and ordinal() (returns the position, 0-based) as well as name() (returns the constant’s name as a string). Enum values are singletons — Status.OPEN is always exactly the same object throughout the entire program, which is why == comparisons on enums (unlike most other objects) are actually safe and common.
Iterating over all values
for (Status s : Status.values()) {
System.out.println(s.name() + " (position " + s.ordinal() + "): " + s.getDescription());
}
// Convert String -> Enum (throws IllegalArgumentException for an invalid name)
Status found = Status.valueOf("DONE");ordinal() is tempting but risky when order is persisted outside the program (e.g. in a database) — if a new constant is later inserted IN THE MIDDLE of the enum definition, all subsequent ordinal() values shift, which suddenly misinterprets stored values. For persisted data, name() (the constant’s name as a string) is the more stable, robust choice.
Enums with abstract methods — different behaviour per constant
An advanced but powerful pattern: every enum constant can bring its OWN implementation of an abstract method, similar to polymorphism:
enum Operation {
PLUS { public int calculate(int a, int b) { return a + b; } },
MINUS { public int calculate(int a, int b) { return a - b; } };
public abstract int calculate(int a, int b);
}
System.out.println(Operation.PLUS.calculate(3, 4)); // 7In many cases this replaces a switch branch that would otherwise be needed over the enum value, and keeps the respective logic right next to the corresponding constant.
EnumMap and EnumSet
For collections whose keys/elements are enum values, EnumMap and EnumSet offer significantly better performance than HashMap/HashSet, because internally they use a simple array instead of a hash table (the position in the array corresponds to the ordinal() value):
EnumMap<Status, Integer> count = new EnumMap<>(Status.class);
count.put(Status.OPEN, 5);
count.put(Status.DONE, 12);
EnumSet<Status> activeStatuses = EnumSet.of(Status.OPEN, Status.IN_PROGRESS);See also: Constants (final), Switch Statement, Classes