Advanced Enums
Advanced Enums
The previous two lessons introduced enum syntax and showed how to attach fields, constructors, and methods to constants. This lesson pushes further into three areas that separate intermediate Java programmers from beginners: constant-specific method bodies, and the two specialized collections EnumSet and EnumMap. Along the way you will also see how values() and valueOf() work under the hood.
Constant-Specific Method Bodies
Every enum constant can override an abstract (or concrete) method declared in the enum body. This technique is called a constant-specific class body, and it lets each constant carry its own implementation rather than forcing a giant switch statement in the caller.
Consider a simple order-processing system where each shipping carrier calculates its cost differently:
Usage is clean — the caller never needs to know which carrier it is talking to:
switch on an enum inside a helper method is fragile: adding a new constant means finding and updating every switch. With constant-specific bodies, you add the constant and its implementation together — the compiler forces you to provide the method, so nothing is forgotten.
You can also override a concrete (non-abstract) method in only some constants, letting the rest fall back to the default:
values() and valueOf()
The compiler silently generates two static methods on every enum:
values()— returns a new array containing all constants in declaration order. Use it to iterate.valueOf(String name)— finds a constant by its exact name; throwsIllegalArgumentExceptionif the name does not match.
valueOf is case-sensitive. Carrier.valueOf("express") throws IllegalArgumentException at runtime. Always normalise the input first: Carrier.valueOf(input.toUpperCase()), or wrap the call in a try-catch.
Every enum also inherits name() (returns the constant's declared name as a String) and ordinal() (returns its zero-based position). Rely on name() for display and serialisation; be careful with ordinal() because inserting a constant in the middle changes all following ordinals.
EnumSet — Efficient Bit Sets for Enum Constants
EnumSet<E extends Enum<E>> is a Set implementation optimised specifically for enum constants. Internally it uses a single long bitmask (or two for enums with more than 64 constants), making every operation — add, contains, remove — an O(1) bit manipulation instead of a hash lookup.
EnumSet over HashSet whenever your set elements are enum constants. It is faster, uses less memory, and its toString() output is always in declaration order, making debugging easier.
EnumMap — Ordered, Fast Map Keyed by Enum Constants
EnumMap<K extends Enum<K>, V> is the companion to EnumSet. It stores values in an internal array indexed by each constant's ordinal(), giving O(1) access with near-zero overhead and always iterating in declaration order.
A common real-world pattern is combining EnumMap with constant-specific method bodies. Store a strategy (a lambda or a method reference) per constant, then dispatch through the map instead of a switch:
Putting It Together
Advanced enums are more than named constants — they are a lightweight object-oriented pattern. By combining constant-specific method bodies with EnumSet and EnumMap, you can model domain rules clearly, avoid fragile switch statements scattered across the codebase, and benefit from the performance characteristics of enum-optimised collections. In the next lesson you will meet Records, another modern Java feature that pairs naturally with enums.