Skip to content
elephantoo

Enums

Lesson 19 of 43 14 min read

Type-safe constants with fields, constructors, methods, switch, values(), EnumMap and EnumSet.


Some variables can only ever hold one of a small, fixed set of values: the days of the week, the status of an order, the suit of a playing card. Beginners often use int or String constants for these:

Java
static final int PENDING = 0, SHIPPED = 1, DELIVERED = 2;
int status = 7;              // compiles, but is meaningless
String s = "shiped";         // typo compiles too

An enum (enumerated type) defines a type whose only possible values are the constants you list. The compiler rejects anything else, your IDE autocompletes the options, and you get a set of useful methods for free.

Declaring and using an enum#

EnumBasics.java
public class EnumBasics {
    public static void main(String[] args) {
        Day today = Day.SATURDAY;
        System.out.println(today);
        System.out.println(today == Day.SATURDAY);
        System.out.println(today.name() + " is at position " + today.ordinal());

        for (Day d : Day.values()) {
            System.out.print(d + " ");
        }
        System.out.println();

        Day parsed = Day.valueOf("MONDAY");
        System.out.println(parsed.compareTo(Day.FRIDAY) < 0);

        try {
            Day.valueOf("monday");
        } catch (IllegalArgumentException e) {
            System.out.println("Bad name: " + e.getMessage());
        }
    }
}

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Output
SATURDAY
true
SATURDAY is at position 5
MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY 
true
Bad name: No enum constant Day.monday

Every enum automatically gets:

MethodReturns
values()an array of all constants, in declaration order
valueOf(String)the constant with that exact name, or throws IllegalArgumentException
name()the constant's name as declared
ordinal()its zero-based position
compareTo(other)compares by declaration order
toString()the name by default (can be overridden)

Each constant is a single object created once by the JVM, so compare enums with ==. Constants are written in UPPER_SNAKE_CASE by convention.

Enums in switch#

Enums and switch fit together perfectly. Inside the cases you write the bare constant name. With a modern switch expression (Java 14+), the compiler checks that you covered every constant, so no default is needed:

EnumSwitch.java
public class EnumSwitch {
    static String plan(Day d) {
        return switch (d) {
            case SATURDAY, SUNDAY -> "Weekend: relax";
            case FRIDAY -> "Almost there";
            case MONDAY, TUESDAY, WEDNESDAY, THURSDAY -> "Work day";
        };                                     // exhaustive: no default required
    }

    public static void main(String[] args) {
        System.out.println(plan(Day.MONDAY));
        System.out.println(plan(Day.FRIDAY));
        System.out.println(plan(Day.SUNDAY));
    }
}

enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
Output
Work day
Almost there
Weekend: relax

If you later add a constant like HOLIDAY, every exhaustive switch expression that doesn't handle it becomes a compile error, which points you at every place that needs updating. That is a great safety net.

Fields, constructors and methods#

Enums are full classes. Each constant can carry data, passed to a constructor:

Planets.java
public class Planets {
    public static void main(String[] args) {
        double earthWeight = 70;
        double mass = earthWeight / Planet.EARTH.surfaceGravity();
        for (Planet p : Planet.values()) {
            System.out.printf("Weight on %-7s %6.2f kg-force%n", p.displayName(), p.surfaceWeight(mass));
        }
    }
}

enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    EARTH(5.976e+24, 6.37814e6),
    JUPITER(1.9e+27, 7.1492e7);           // semicolon needed before fields/methods

    private static final double G = 6.67300E-11;

    private final double mass;            // kg
    private final double radius;          // m

    Planet(double mass, double radius) {  // always private (implicitly)
        this.mass = mass;
        this.radius = radius;
    }

    double surfaceGravity() {
        return G * mass / (radius * radius);
    }

    double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }

    String displayName() {
        return name().charAt(0) + name().substring(1).toLowerCase();
    }
}
Output
Weight on Mercury  26.44 kg-force
Weight on Earth    70.00 kg-force
Weight on Jupiter 177.14 kg-force

Rules:

  • The constants must come first, followed by a ; if anything else follows.
  • Enum constructors are always private; you can never call new Planet(...).
  • Fields should be final. Enum constants are shared globally, so mutable state in them behaves like a global variable.

A practical example: order status with codes

OrderStatus.java
public class OrderStatus {
    public static void main(String[] args) {
        Status s = Status.fromCode("SHP");
        System.out.println(s + " -> " + s.label());
        System.out.println(s.canCancel());
        System.out.println(Status.PLACED.canCancel());
    }
}

enum Status {
    PLACED("PLC", "Order placed"),
    SHIPPED("SHP", "On the way"),
    DELIVERED("DLV", "Delivered");

    private final String code;
    private final String label;

    Status(String code, String label) {
        this.code = code;
        this.label = label;
    }

    String code() { return code; }
    String label() { return label; }

    boolean canCancel() { return this == PLACED; }

    static Status fromCode(String code) {
        for (Status s : values()) {
            if (s.code.equals(code)) return s;
        }
        throw new IllegalArgumentException("Unknown status code: " + code);
    }
}
Output
SHIPPED -> On the way
false
true

Storing a stable code (or name()) in a database is much safer than storing ordinal(), which changes if you ever reorder the constants.

Constant-specific behaviour#

Each constant can have its own body that overrides a method. Combined with an abstract method, the compiler forces every constant to implement it:

Calculator.java
public class Calculator {
    public static void main(String[] args) {
        for (Op op : Op.values()) {
            System.out.println("8 " + op.symbol + " 2 = " + op.apply(8, 2));
        }
    }
}

enum Op {
    ADD("+")      { int apply(int a, int b) { return a + b; } },
    SUBTRACT("-") { int apply(int a, int b) { return a - b; } },
    MULTIPLY("*") { int apply(int a, int b) { return a * b; } },
    DIVIDE("/")   { int apply(int a, int b) { return a / b; } };

    final String symbol;

    Op(String symbol) { this.symbol = symbol; }

    abstract int apply(int a, int b);
}
Output
8 + 2 = 10
8 - 2 = 6
8 * 2 = 16
8 / 2 = 4

Enums can also implement interfaces (enum Op implements IntBinaryOperator), but they cannot extend a class, because every enum already extends java.lang.Enum.

EnumSet and EnumMap#

The java.util package has collections tuned specially for enums. Internally they use bit flags and arrays indexed by ordinal, so they are faster and more compact than HashSet/HashMap, and they keep the constants in declaration order:

EnumCollections.java
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;

public class EnumCollections {
    public static void main(String[] args) {
        Set<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
        Set<Day> weekdays = EnumSet.complementOf(EnumSet.copyOf(weekend));
        Set<Day> midweek = EnumSet.range(Day.TUESDAY, Day.THURSDAY);

        System.out.println(weekend);
        System.out.println(weekdays);
        System.out.println(midweek);
        System.out.println(weekend.contains(Day.MONDAY));

        Map<Day, Integer> steps = new EnumMap<>(Day.class);
        steps.put(Day.WEDNESDAY, 9200);
        steps.put(Day.MONDAY, 7500);
        steps.put(Day.SUNDAY, 3100);
        System.out.println(steps);              // always in declaration order
    }
}

enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
Output
[SATURDAY, SUNDAY]
[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
[TUESDAY, WEDNESDAY, THURSDAY]
false
{MONDAY=7500, WEDNESDAY=9200, SUNDAY=3100}

EnumSet is the modern replacement for "bit flag" int constants: EnumSet.of(Permission.READ, Permission.WRITE) reads far better than READ | WRITE.

Enums as singletons#

Because the JVM guarantees each constant exists exactly once, a one-constant enum is the simplest thread-safe singleton:

Java
enum AppConfig {
    INSTANCE;

    private final String version = "2.4.1";
    String version() { return version; }
}

// usage: AppConfig.INSTANCE.version()

Common mistakes#

  • Using int or String constants where an enum would make invalid values impossible.
  • Passing user input straight to valueOf without handling IllegalArgumentException (or without normalising case, for example Day.valueOf(input.strip().toUpperCase())).
  • Persisting ordinal() values.
  • Writing case Day.MONDAY: in older Java versions; in a switch on an enum, plain case MONDAY is the normal style (qualified names are only allowed from Java 21).
  • Forgetting the ; after the last constant when the enum has fields or methods.
  • Putting mutable fields in enum constants.

What's next#

Our programs now have many classes. The next lesson, packages, imports and JARs, shows you how to organise them into folders and namespaces and ship them as a single file.

Check your understanding

Quick quiz

0/3 answered
  1. 1.What does Size.valueOf("huge") do if Size has constants SMALL, MEDIUM, LARGE?

  2. 2.Which is the safest way to compare two enum values?

  3. 3.Why is relying on ordinal() to store an enum in a database a bad idea?

Finished reading?

Mark this lesson complete to track your progress.