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.
| Member | What 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 |
compareTo | Difference of ordinals; enums are Comparable |
getDeclaringClass() | The enum type, even for constants with bodies |
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
- JLS section 8.9: Enum Classes
- EnumInDepth.java and EnumPatterns.java (the demo programs)
- Records in Java
No Comments yet!