Enums and switch expressions
Enums as real classes with fields and an order, and the switch expression that replaced fall-through with exhaustiveness checks.
After this lesson you can
- Give an enum fields, a constructor and methods
- Say what valueOf does with an unknown name and why ordinal() should not be stored
- Write a switch expression with arrow labels, multiple labels and yield
- Explain how exhaustiveness turns a forgotten case into a compile error
An enum is a class with a fixed set of instances, created once when
the class loads. Each constant is a real object, so an enum can have
fields, a constructor (always private) and methods, and == is safe
for comparing constants because there is only ever one of each.
enum Size {
SMALL("S"), MEDIUM("M"), LARGE("L");
private final String label;
Size(String label) { this.label = label; }
String label() { return label; }
}
Every enum gets values() (the constants in declaration order),
valueOf(String) (exact, case-sensitive lookup by name), name() and
ordinal() (the position, from 0). Enums implement Comparable, and
their natural order is declaration order.
import java.util.*; public class Solution { enum Size { SMALL("S"), MEDIUM("M"), LARGE("L"); private final String label; Size(String label) { this.label = label; } String label() { return label; } } public static List<String> describe() { List<String> out = new ArrayList<>(); for (Size s : Size.values()) { out.add(s.name() + " ordinal=" + s.ordinal() + " label=" + s.label()); } out.add("SMALL < LARGE: " + (Size.SMALL.compareTo(Size.LARGE) < 0)); out.add("valueOf(\"MEDIUM\") == MEDIUM: " + (Size.valueOf("MEDIUM") == Size.MEDIUM)); try { Size.valueOf("medium"); } catch (IllegalArgumentException e) { out.add("valueOf(\"medium\"): " + e.getMessage()); } return out; }}EnumMap and EnumSet are the collections to reach for with enum keys:
an EnumMap is an array indexed by ordinal and an EnumSet a bit
vector, so both are compact and fast, and both always iterate in
declaration order.
switch expressions
The classic switch statement falls through: without a break,
execution continues into the next case. Java 14 made the switch
expression standard: arrow labels never fall through, one case can
list several labels, and the whole switch produces a value.
import java.util.*; public class Solution { enum Level { LOW, MEDIUM, HIGH } static String oldStyle(Level level) { String actions = ""; switch (level) { case HIGH: actions += "page "; // forgot break: falls into MEDIUM case MEDIUM: actions += "email "; break; case LOW: actions += "log "; } return actions.trim(); } static String newStyle(Level level) { return switch (level) { case HIGH -> "page"; case MEDIUM -> "email"; case LOW -> "log"; }; } public static List<String> describe() { return List.of( "statement HIGH -> " + oldStyle(Level.HIGH), "expression HIGH -> " + newStyle(Level.HIGH)); }}int days = switch (month) {
case FEBRUARY -> leapYear ? 29 : 28;
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
default -> {
log("31-day month");
yield 31; // a block produces its value with yield
}
};
A switch expression must be exhaustive: it has to produce a value
for every possible input. Over an enum, listing every constant counts,
so no default is needed — and then adding a constant to the enum
later makes every such switch fail to compile until the new case is
handled. That is the real reason to prefer them: a forgotten case
becomes a compile error instead of a silent no-op. (For a switch over
an enum with no default, the compiler also inserts a runtime check
that throws if a class compiled against an older version of the enum
meets a new constant.)
Common mistakes
- Persisting
ordinal()(in a database or a file). Inserting a constant in the middle renumbers everything after it; storename()or an explicit code field instead. - Calling
valueOfon user input without handlingIllegalArgumentException— it is case-sensitive and throws for any unknown name. - Sorting enum names as strings when declaration order is the meaning
(
L,M,Sinstead ofS,M,L). - A forgotten
breakin an old-style statement — the second example above.
import java.util.*; public class Solution { enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY } public static List<String> describe() { Map<Day, Integer> meetings = new EnumMap<>(Day.class); meetings.put(Day.FRIDAY, 1); meetings.put(Day.MONDAY, 3); meetings.put(Day.WEDNESDAY, 2); List<String> names = new ArrayList<>(List.of("FRIDAY", "MONDAY", "WEDNESDAY")); Collections.sort(names); // alphabetical List<Day> days = new ArrayList<>(List.of(Day.FRIDAY, Day.MONDAY, Day.WEDNESDAY)); Collections.sort(days); // declaration order return List.of("EnumMap: " + meetings, "names sorted: " + names, "days sorted: " + days); }}What interviewers ask
- Why is an enum a good way to implement a singleton? The JVM creates each constant exactly once, thread-safely, and serialization and reflection cannot create a second one.
- What does a switch expression give you over a switch statement? No fall-through, a result value, several labels per case, and a compile error when a case is missing.
- Why use
EnumMapinstead ofHashMapfor enum keys? Array-backed by ordinal: no hashing, less memory, and a predictable iteration order.
Exercise · Pro
Sort sizes the way the enum declares them
Exercises are free in the first 3 lessons of every course. From here on they are part of Pro and Sprint; the lesson and its runnable examples stay open.
Upgrade