Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java enums: finite states without ordinal storage

Last updated: 28 Sept 20263 min read
tutorial
IntermediateBy AITrove Editorial

An enum defines a fixed set of named instances that can carry fields and behavior while remaining a distinct Java type.

Names are safer than magic integers

An order status should not be any arbitrary integer. An enum lets a method accept only the defined state type. That catches accidental mixing of status values with unrelated counts or IDs at compile time.

The enum below assigns an explicit external code to each state. It does not persist ordinal(), because ordinal is the declaration position. Reordering or adding constants can change that position without changing the intended business meaning.

valueOf expects the exact constant name and throws if the name is not present. A client-facing parser often needs stable external codes, case rules, and an unknown-value policy rather than directly exposing Java constant names.

Transitions still need validation

A finite list of states does not prove every transition is valid. Moving from NEW to PAID may be allowed, while moving from CANCELLED to PAID may require a separate recovery policy.

The demonstration validates one transition before printing its external code. It keeps the transition policy next to the state logic so unsupported cases are visible. A real order system would also need durable transaction boundaries around state changes.

Enum values are single instances, but their fields could be mutable if designed that way. Avoid per-order data inside enum constants. Store an order’s data in its own object and let the enum describe only its state.

Working program

Java
public class OrderStateCodes {
    enum OrderState {
        NEW("new"), PAID("paid"), CANCELLED("cancelled");
        private final String externalCode;
        OrderState(String externalCode) { this.externalCode = externalCode; }
        String externalCode() { return externalCode; }
        boolean canMoveTo(OrderState next) {
            return this == NEW && (next == PAID || next == CANCELLED);
        }
    }
    public static void main(String[] args) {
        OrderState current = OrderState.NEW;
        OrderState requested = OrderState.PAID;
        if (!current.canMoveTo(requested)) {
            throw new IllegalStateException("Rejected order transition");
        }
        System.out.println(requested.externalCode());
        System.out.println(OrderState.CANCELLED.canMoveTo(OrderState.PAID));
    }
}

Output

Output
paid
false

Cost and design choices

Comparing enum instances with == is a constant identity comparison. A small explicit transition check also has constant cost. A larger workflow might use an EnumSet or transition map, which keeps rules readable without a long branch chain.

The enum instances are shared, not allocated once per order. An order field holds a reference to the selected instance. Per-order payload storage belongs elsewhere.

Adding a new constant is an API evolution event. Consumers with exhaustive assumptions, stored external codes, or switch defaults need a deliberate compatibility plan.

Connected lessons

Continue with Object state, Input failures, Shared fields.

Common Mistakes

  • Do not persist ordinal as a stable business identifier.
  • Do not put mutable per-request data in enum fields.
  • Do not use an unchecked valueOf call as the entire external input validation strategy.
java
enums
Storage details