Skip to main content

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)).

Quiz

Enums vs int constants

1 / 2

Reordering 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).

Quiz

EnumSet vs bit fields

1 / 2

applyStyles(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.

Quiz

EnumMap vs ordinal arrays

1 / 2

Set<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​

ItemMove
34Enum types over int/string constants; constant-specific methods
35Associated values in fields — never ordinal()
36EnumSet over bit fields
37EnumMap over ordinal-indexed arrays

Next: Extensible Enums and Annotations (Items 38–41).