Skip to main content

Enum in Java: Methods, Fields, EnumMap, Switch and Singleton Explained

Enum in Java explained with a runnable program: generated methods, fields and constructors, constant-specific bodies, switch and pattern matching, EnumMap and EnumSet, and the enum singleton.

A Java enum is a special class with a fixed set of named instances, created once when the class loads. Because the set is closed, the compiler can check switch statements over it, and the JVM guarantees that each constant is a true singleton. Enums can have fields, constructors, methods and even a different body per constant, and they work as keys in the fast EnumMap.
MemberWhat it does
values()New array of all constants in declaration order
valueOf(String)Constant by exact name; throws IllegalArgumentException otherwise
name() / toString()Declared name / same by default, overridable
ordinal()Position, starting at 0; avoid depending on it
compareToDifference of ordinals; enums are Comparable
getDeclaringClass()The enum type, even for constants with bodies
enum Opextends java.lang.Enum ADD (class Op$1) MUL (class Op$2) one instance each
Tested against. Temurin 25.0.4.1. All output comes from two programs that are not in a repository: download EnumInDepth.java (javac -g EnumInDepth.java && java EnumInDepth) and download EnumPatterns.java. Pattern matching in switch is final since Java 21.

The methods the compiler generates

enum Level { LOW, MEDIUM, HIGH }
Level.values();             // [LOW, MEDIUM, HIGH]
Level.valueOf("HIGH");      // HIGH
Level.valueOf("high");      // IllegalArgumentException
values()      : [LOW, MEDIUM, HIGH]
valueOf(HIGH) : HIGH, ordinal=2, name=HIGH
compareTo     : -2 (ordinal difference)
valueOf("high") : No enum constant EnumInDepth.Level.high
== is a singleton per constant: true

Source: EnumInDepth.java. Names are case sensitive, so valueOf("high") fails with a message that names the constant and the enum. Because each constant is a single instance, comparing with == is correct and null safe; you do not need equals.

Fields, constructors and methods

An enum constant can carry data, supplied through a constructor that is implicitly private. The classic example is a planet with mass and radius:

enum Planet {
    MERCURY(3.303e+23, 2.4397e6), EARTH(5.976e+24, 6.37814e6);
    final double mass, radius;
    Planet(double mass, double radius) { this.mass = mass; this.radius = radius; }
    double surfaceGravity() { return 6.67300E-11 * mass / (radius * radius); }
}
Earth surface gravity = 9.80 m/s^2, Mercury = 3.70 m/s^2

Keep enum fields final. A mutable enum field is global state shared by the whole JVM. Download: EnumInDepth.java.

A different body per constant

When behaviour differs by constant, declare an abstract method and implement it in a body after each constant. This replaces long switch chains with polymorphism.

enum Op {
    ADD("+") { int apply(int a, int b) { return a + b; } },
    MUL("*") { int apply(int a, int b) { return a * b; } };
    final String sym;
    Op(String sym) { this.sym = sym; }
    abstract int apply(int a, int b);
}
3 + 4 = 7   getClass=EnumInDepth$Op$1  getDeclaringClass=EnumInDepth$Op
3 * 4 = 12   getClass=EnumInDepth$Op$2  getDeclaringClass=EnumInDepth$Op

Each constant with a body is an instance of its own anonymous subclass (Op$1, Op$2), which is why getClass() differs from getDeclaringClass(). Use getDeclaringClass() when you need the enum type itself, for example as a map key. Download: EnumInDepth.java.

Enums in switch

A switch expression over an enum must cover every constant or have a default. The arrow form needs no break and several constants can share a case.

String kind = switch (d) { case SAT, SUN -> "weekend"; case MON, TUE, WED, THU, FRI -> "weekday"; };
SAT is a weekend
WED is a weekday

Leave out default when you cover every constant: add a new constant later and the compiler will point to every switch that now needs updating. Source: EnumInDepth.java.

Enums with pattern matching (Java 21)

An enum can implement a sealed interface and take part in a pattern-matching switch, mixing constants and records. Qualified constant names such as Keyword.IF are allowed as case labels since Java 21.

sealed interface Token permits Keyword, Number {}
enum Keyword implements Token { IF, ELSE, WHILE }
record Number(int value) implements Token {}

case Keyword.IF -> "the if keyword";
case Keyword k -> "another keyword: " + k.name().toLowerCase();
case Number(int v) when v < 0 -> "negative number " + v;
case Number(int v) -> "number " + v;
the if keyword
another keyword: while
negative number -3
number 8

Source: EnumPatterns.java. The switch has no default and still compiles, because the compiler knows Keyword and Number are the only permitted subtypes. See sealed classes and exhaustive switch and pattern matching in depth.

EnumMap and EnumSet

When keys are enum constants, use EnumMap and EnumSet instead of HashMap and HashSet. They are backed by arrays and bit sets indexed by ordinal, so they are compact and fast, and they iterate in declaration order.

EnumMap<Day, String> plan = new EnumMap<>(Day.class);
plan.put(Day.FRI, "deploy"); plan.put(Day.MON, "standup"); plan.put(Day.WED, "review");
EnumMap order (by ordinal, not insertion): {MON=standup, WED=review, FRI=deploy}
range(TUE, THU)    : [TUE, WED, THU]
complementOf(weekend): [MON, TUE, WED, THU, FRI]
noneOf / allOf     : [] / size 7

Source: EnumInDepth.java. The entries went in as FRI, MON, WED and came out as MON, WED, FRI. Compare with the general-purpose HashMap, whose order depends on hashes, and with the other collection types in the collections interview questions.

The enum singleton

A one-constant enum is the simplest safe singleton. The JVM guarantees one instance, creation is thread safe, and, unlike a normal class, it survives both reflection and serialization attacks.

enum Registry { INSTANCE; private int hits; int hit() { return ++hits; } }
hits via INSTANCE : 3
reflection       : IllegalArgumentException: Cannot reflectively create enum objects
serialization    : same instance after round trip = true

Source: EnumInDepth.java. Reflection cannot construct a second instance, and deserialization returns the existing constant instead of a copy. Regular singletons need extra code to resist both; background in why Java serialization is dangerous and the static keyword.

Going deeper: what an enum cannot do

An enum cannot extend another class, because it already extends java.lang.Enum; it can implement interfaces. Its constructor cannot be public, and you cannot instantiate it with new. An enum singleton also cannot be lazily initialised or subclassed, and is a poor fit when you want to swap implementations in tests; for that, depend on an interface. See abstract class vs interface.

Common mistakes

Storing ordinal() in a database

Reordering or inserting a constant silently changes the meaning of existing rows. Store the name, or an explicit code field.

Trusting valueOf with user input

It throws on a miss and is case sensitive. Wrap it, or look up through a static map you build once.

Mutable enum fields

Each constant is shared by the whole JVM, so mutating its state is global state. Make fields final.

Adding a default to a switch you want checked

A default hides the compiler’s reminder when you add a constant. Cover each constant and leave it out.

Using getClass() instead of getDeclaringClass()

For constants with bodies, getClass() returns an anonymous subclass such as Op$1, as the output above shows.

Common interview questions

Can an enum extend a class or implement an interface?

It can implement interfaces, including sealed ones, but cannot extend a class because it already extends java.lang.Enum.

Why is an enum a good singleton?

The JVM creates each constant once, thread safely, and refuses to create more through reflection or deserialization, as the output above shows.

Can an enum constructor be public?

No. It is implicitly private; declaring it public or protected is a compile error.

What is the difference between ordinal() and compareTo()?

ordinal() is the constant’s index; compareTo returns the difference of two ordinals (-2 for LOW against HIGH above).

Why use EnumMap instead of HashMap?

It is an array indexed by ordinal: smaller, faster, and iterates in declaration order.

FAQ

Can I compare enums with ==?

Yes, and you should. Each constant is a single instance, so == is correct and cannot throw a NullPointerException.

Can an enum have abstract methods?

Yes, if every constant provides a body implementing them, as Op does above.

Can I add constants at run time?

No. The set is fixed at compile time. Use a normal class or a map if the set must change.

When to use an enum. Whenever a value comes from a small fixed set: states, types, modes, days. Prefer an enum to string or int constants, because the compiler checks them. Avoid it for sets that change at run time. Next in this series: wrapper classes in Java.

Further reading

No Comments yet!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.