EMZETT.
Login

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)); // 7

In 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