Enums
A class restricted to a fixed, named set of instances, decided once at compile time.
Instead of passing the string "PENDING" around and misspelling it in one call site out
of forty, you declare OrderStatus.PENDING and the compiler refuses to let anything else
exist under that name.
An enum is still a class
Under the hood, an enum is still a class - the compiler just pre-builds exactly one object per named value and seals the constructor. That is why an enum constant can carry its own fields and override its own methods, the same as any other object:
Why an enum beats a string or an int constant
Two things this buys you that a raw constant cannot:
- Compile-time safety. The compiler rejects an out-of-range value at the call site
instead of at 3 a.m. in production -
OrderStatus.SHIPED(typo intact) is a build failure, while"SHIPED"compiles fine and fails a live order somewhere downstream. - Exhaustiveness checking. A
switchover an enum can be checked for completeness, so addingRETURNEDlater surfaces every place that forgot about it - the compiler does the "find every usage" search that a code review would otherwise have to do by hand.
The price is symmetrical: adding a value means touching the enum's own declaration, and
every exhaustive switch over it now needs a new case, which is exactly the kind of
"vary here" instability the State pattern exists to route through polymorphism instead,
once the per-constant behavior outgrows a switch.
Enum constants with fields and per-constant behavior
An enum constant is not limited to a bare name. It can carry its own field values, set through the enum's own constructor, so that each constant is a distinct object with its own data rather than an interchangeable label:
Some languages go further and let each constant override a method body individually, rather than branching inside one shared method:
Operation.PLUS.apply(2, 3) and Operation.MINUS.apply(2, 3) each run their own method
body - no switch at all. This is a small, contained dose of the same idea the State
pattern applies at a larger scale: behavior that varies per value lives on the value,
not in a branch that inspects the value.
Enum-as-singleton
Because the compiler guarantees exactly one instance per constant, a single-constant enum is a common, safe way to implement the Singleton pattern in languages like Java - it gets serialization safety and protection against reflection-based double-construction for free, which a hand-rolled singleton class has to earn with extra code:
Iterating and looking up by name
Every enum comes with two capabilities beyond ordinary classes: enumerating every constant that exists, and looking one up by the exact name it was declared with.
values() is how an exhaustive UI dropdown or a validation routine can stay in sync with
the enum automatically - add RETURNED to the declaration and every loop over values()
picks it up without being told. valueOf is the inverse of toString() for the default
case, and it is the reason enum names are usually kept as the literal SCREAMING_CASE
strings a serialized payload would contain.
Try it yourself: sketch an enum for a traffic light - RED, YELLOW, GREEN.
Which method would you want each constant to override so its behavior differs per
value, the way refundRate() differs per OrderStatus? Then imagine a sixth status,
RETURNED, being added to OrderStatus - which of the checks above would catch every
switch that needs updating, and which would stay silent?
Check yourself
Why can an enum constant override a method, the same way a subclass can?