liveBook · Manning

Enum

As explained in 100 Java Mistakes and How to Avoid Them

An enum type is a Java class-like type that defines a fixed set of named constants.

An enum defines a fixed set of named constants, but those constants are ordinary initialization-time objects rather than values that exist before their enum class is initialized. This matters when constructors compute fields from other enum constants: a reference can observe the other enum before its constants are ready and permanently receive null. section 3.9.4

enum Pet {
  DOG(Voice.BARK), 
  CAT(Voice.MEOW);

  final Voice voice;
  Pet(Voice voice) { this.voice = voice; }
}

enum Voice {
  BARK(Pet.DOG),
  MEOW(Pet.CAT);

  final Pet owner;
  Voice(Pet owner) { this.owner = owner; }
}

These mutually dependent constructors form an initialization loop. If Pet starts first, Voice is still uninitialized; if Voice starts first, Pet is still uninitialized. One direction may appear to work while the reverse field is null. section 3.9.4

Avoid constructor-time cycles between enums. If a relationship must be established this way, make the affected field nonfinal and assign it after constructor invocation, when both enum constant sets exist. Do not assume enum utilities are safe during constant construction: values() is unavailable until all constants have been created. section 3.9.4

A switch over the enum currently being initialized can cause the same failure on Java 20 and earlier. Such switches may implicitly invoke values(), because compilation desugars an enum switch to a numerical switch with a mapping based on ordinal values. The resulting class initialization can throw NullPointerException. Use ordinal()-based logic only where that trade-off is explicitly intended, or avoid switching on the current enum during its construction. section 3.9.4

Enums also model a meaningful finite state better than null. When a method can return “no value” with more than one meaning, encode each meaning as a named constant such as UNKNOWN, UNSPECIFIED, or UNINITIALIZED. The caller can then handle every outcome directly rather than infer what a null meant. section 5.1.1

Model a three-way user decision

A nullable Boolean can blur Yes, No, and Cancel; treating null as false can accidentally continue an operation after cancellation. Define an enum such as UserAnswer { YES, NO, CANCEL }, or model the requested action directly with REMOVE_FROM_BASKET, MOVE_TO_WISH_LIST, and CANCEL. A switch can then perform the action or deliberately do nothing for cancellation. RoundingMode.UNNECESSARY similarly gives “do not round” an explicit name and documented consequence. section 5.1.1

How 100 Java Mistakes and How to Avoid Them covers it

How deep it goes
Explains initialization ordering, constructor cycles, values() failure, and pre-Java-21 switch desugaring.
Examples
Uses mutually referencing Pet and Voice constants, a DayOfWeek weekend field, user answers, and RoundingMode.UNNECESSARY.
What it assumes
Assumes familiarity with Java constructors, fields, switch expressions, and null handling.
What you can do after
Diagnose/debug enum initialization nulls and model distinct absent-result meanings as named constants.
How it connects
Connects enums to class initialization, constructors, switch expressions, values(), ordinal(), null, and Optional.

Also explained in

Compare how 23 books explain enum →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Java Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.