Enums
Source: Effective Java, 3rd Edition — Chapter 6 by Joshua Bloch (Items 34–37)
Java’s enum types are full classes — one instance per constant, compile-time type safety, namespaces, and toString. This lesson: prefer enums over int/string constants, store associated data in fields (not ordinal()), and use EnumSet / EnumMap instead of bit fields and ordinal indexing.
Item 34: Use enums instead of int constants
Int enum pattern (public static final int APPLE_FUJI = 0) lacks type safety, namespaces, stable binary evolution, printable names, and iteration. String enum pattern is worse — clients hard-code strings with typo risk.
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BLOOD }
Enums are instance-controlled (like singletons, Item 3): no public constructors, no extension, fixed constants. They implement Comparable, Serializable, and solid Object methods.
Rich enums: attach data via final fields + constructor (Planet mass/radius → surfaceWeight). Override toString; pair with fromString via static Map built after constants exist (not from constructors — static fields aren’t ready).
Behavior per constant: prefer constant-specific method implementations (abstract method + body per constant) over switch(this) — adding a constant without a case silently breaks switch enums.
Strategy enum pattern (PayrollDay + nested PayType) when only some constants share behavior — forces an explicit strategy per constant.
Use switch on enums only to augment types you don’t control (e.g. inverse(Operation op)).
Enums vs int constants
1 / 2Reordering int enum constants after clients compiled
Pick an answer to see if you are right.
Item 35: Use instance fields instead of ordinals
Every enum has ordinal() — declaration order. Do not derive business meaning from it:
// DON'T
public int numberOfMusicians() { return ordinal() + 1; }
Reordering constants breaks clients; you cannot reuse numeric values (OCTET and DOUBLE_QUARTET both 8) or skip values without dummy constants.
Fix: store the associated int in a final instance field set in the constructor:
TRIO(3), QUARTET(4), OCTET(8), DOUBLE_QUARTET(8), TRIPLE_QUARTET(12);
private final int numberOfMusicians;
ordinal() exists for infrastructure (EnumSet, EnumMap), not application logic.
Item 36: Use EnumSet instead of bit fields
Bit fields (STYLE_BOLD = 1 << 0, OR together, applyStyles(int)) share all int-enum flaws plus opaque printing and fixed width (32/64 bits).
EnumSet — Set implementation backed by a bit vector; as fast as manual bit twiddling, with enum type safety and rich Set API:
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
Accept Set<Style> in APIs (interface type), not only EnumSet<Style> (Item 64).
EnumSet vs bit fields
1 / 2applyStyles(int) with STYLE_BOLD | STYLE_ITALIC
Pick an answer to see if you are right.
Item 37: Use EnumMap instead of ordinal indexing
Don’t index arrays with lifeCycle.ordinal() — unchecked casts, manual labels, silent wrong-slot bugs.
EnumMap — map keyed by enum; internally uses an ordinal-backed array but type-safe API; constructor takes EnumMap(LifeCycle.class) (bounded type token, Item 33).
For pairs of enums, use EnumMap<K, EnumMap<K2, V>> (e.g. phase transitions) instead of Transition[][] indexed by ordinals.
EnumMap vs ordinal arrays
1 / 2Set<Plant>[] buckets = new Set[LifeCycle.values().length]; buckets[p.lifeCycle.ordinal()].add(p)
Pick an answer to see if you are right.
Items 34–37 map
| Item | Move |
|---|---|
| 34 | Enum types over int/string constants; constant-specific methods |
| 35 | Associated values in fields — never ordinal() |
| 36 | EnumSet over bit fields |
| 37 | EnumMap over ordinal-indexed arrays |
Next: Extensible Enums and Annotations (Items 38–41).