Enum¶
1. What is an Enum¶
Enum (enumeration) is a special type in Java used to represent a fixed set of named constants with clear meaning.
Each value in an enum (MONDAY, TUESDAY, ...) is called an enum constant. These are objects, not plain integers or strings.
2. Why It Matters¶
Before enums, developers used int or String constants to represent fixed sets:
// ❌ Old way — int constants
public static final int STATUS_PENDING = 0;
public static final int STATUS_ACTIVE = 1;
public static final int STATUS_INACTIVE = 2;
void setStatus(int status) { ... } // accepts any int — not type-safe
setStatus(99); // compiler won't complain, but 99 is invalid
// ❌ Old way — String constants
public static final String ROLE_ADMIN = "ADMIN";
public static final String ROLE_USER = "USER";
void setRole(String role) { ... } // typos go undetected
setRole("admin"); // differs from "ADMIN" — logic bug, compiler won't catch it
Enum solves all of these problems:
// ✅ Enum — type-safe, expressive, impossible to pass invalid values
public enum OrderStatus { PENDING, ACTIVE, INACTIVE }
public enum Role { ADMIN, USER }
void setStatus(OrderStatus status) { ... }
setStatus(OrderStatus.PENDING); // ✅
setStatus(99); // ❌ compile error immediately
Concrete benefits:
- Type-safe — compiler catches invalid values at compile time
- Readable — code is self-documenting, no comments needed
- Maintainable — add/remove constants in one place, IDE highlights all usages
- Switchable — switch and pattern matching natively support enums
- Singleton guarantee — each constant exists as exactly one instance in the JVM
3. Basic Enum¶
Declaration¶
Convention: enum name in PascalCase, constants in SCREAMING_SNAKE_CASE.
Usage¶
Season current = Season.SUMMER;
// Compare with ==, not .equals()
if (current == Season.SUMMER) {
System.out.println("It's summer!");
}
name() and ordinal()¶
Every enum constant has two built-in properties:
Season s = Season.AUTUMN;
System.out.println(s.name()); // "AUTUMN" — the constant's name as a String
System.out.println(s.ordinal()); // 2 — position in declaration (zero-based)
Don't rely on ordinal()
ordinal() returns the declaration position. If you insert a new constant in the middle later, every ordinal after it shifts — breaking any logic that depended on the old values.
values() and valueOf()¶
// values() — returns an array of all constants in declaration order
for (Season s : Season.values()) {
System.out.println(s); // SPRING, SUMMER, AUTUMN, WINTER
}
// valueOf() — converts a String to an enum constant (case-sensitive)
Season s = Season.valueOf("WINTER"); // Season.WINTER
Season x = Season.valueOf("winter"); // ❌ IllegalArgumentException
4. Enum with Fields and Constructor¶
Enum is a special class — it can have fields, a constructor, and methods.
public enum Planet {
MERCURY(3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6);
private final double mass; // kg
private final double radius; // m
Planet(double mass, double radius) { // constructor is always private
this.mass = mass;
this.radius = radius;
}
static final double G = 6.67300E-11;
public double surfaceGravity() {
return G * mass / (radius * radius);
}
public double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
}
double earthWeight = 75.0;
double mass = earthWeight / Planet.EARTH.surfaceGravity();
for (Planet p : Planet.values()) {
System.out.printf("Weight on %s: %.2f%n", p, p.surfaceWeight(mass));
}
// Weight on MERCURY: 28.33
// Weight on EARTH: 75.00
// ...
Enum constructors are always private
Even without an explicit private keyword, enum constructors are always private — no one can create enum instances from outside. This is how Java guarantees the singleton property of each constant.
5. Real-World Example — OrderStatus¶
This pattern appears in almost every backend application:
public enum OrderStatus {
PENDING("Awaiting processing"),
CONFIRMED("Confirmed"),
SHIPPING("In transit"),
DELIVERED("Delivered"),
CANCELLED("Cancelled");
private final String displayName;
OrderStatus(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
public boolean isFinal() {
return this == DELIVERED || this == CANCELLED;
}
}
OrderStatus status = OrderStatus.SHIPPING;
System.out.println(status.getDisplayName()); // In transit
System.out.println(status.isFinal()); // false
6. Enum in switch¶
Enums pair naturally with switch — the compiler checks all cases when using enhanced switch.
Traditional switch¶
Day day = Day.SATURDAY;
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
System.out.println("Weekday");
break;
case SATURDAY:
case SUNDAY:
System.out.println("Weekend");
break;
}
Enhanced switch (Java 14+ — preferred)¶
String type = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
case SATURDAY, SUNDAY -> "Weekend";
};
System.out.println(type); // Weekend
Enhanced switch with enum — exhaustiveness check
If the switch expression doesn't cover all enum constants, the compiler raises an error. This is one of the most powerful reasons to use enum + switch for fixed-set logic.
7. Enum with Abstract Methods¶
Each constant can override an abstract method — giving each constant its own behavior.
public enum Operation {
PLUS("+") {
@Override
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
@Override
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
@Override
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
@Override
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
public abstract double apply(double x, double y);
@Override
public String toString() { return symbol; }
}
double x = 10, y = 3;
for (Operation op : Operation.values()) {
System.out.printf("%.1f %s %.1f = %.1f%n", x, op, y, op.apply(x, y));
}
// 10.0 + 3.0 = 13.0
// 10.0 - 3.0 = 7.0
// 10.0 * 3.0 = 30.0
// 10.0 / 3.0 = 3.3
This pattern completely replaces long if-else chains — adding a new operation only requires a new constant, no existing logic changes.
8. EnumSet and EnumMap¶
Two collections optimized specifically for enums — faster than HashSet and HashMap.
import java.util.EnumSet;
import java.util.EnumMap;
// EnumSet — a set of enum constants
EnumSet<Day> weekdays = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY,
Day.THURSDAY, Day.FRIDAY);
EnumSet<Day> weekend = EnumSet.complementOf(weekdays); // SATURDAY, SUNDAY
System.out.println(weekdays.contains(Day.MONDAY)); // true
System.out.println(weekend.contains(Day.SATURDAY)); // true
// EnumMap — a map with enum constants as keys
EnumMap<Day, String> schedule = new EnumMap<>(Day.class);
schedule.put(Day.MONDAY, "Standup meeting");
schedule.put(Day.WEDNESDAY, "Code review");
schedule.put(Day.FRIDAY, "Demo");
System.out.println(schedule.get(Day.MONDAY)); // Standup meeting
Use EnumSet/EnumMap when the key is an enum
EnumSet uses a bit-vector internally — each bit corresponds to one constant by ordinal — making contains(), add(), remove() extremely fast. EnumMap uses an array indexed by ordinal. Both are significantly faster than their generic counterparts for enum keys.
9. Full Example¶
Verified
Full compilable source: EnumDemo.java
- Factory method
fromCode()— a common pattern to look up an enum constant from a raw value (int, String). Frequently used when deserializing data from a database or external API.
10. Common Mistakes¶
Mistake 1 — Using ordinal() for logic¶
// ❌ ordinal() shifts when a new constant is inserted in the middle
public enum Status { PENDING, ACTIVE, INACTIVE }
int code = status.ordinal(); // saved to DB
// Later someone adds SUSPENDED between ACTIVE and INACTIVE
// → INACTIVE.ordinal() changes from 2 to 3, all old data maps incorrectly
// ✅ Use a dedicated field instead
public enum Status {
PENDING(0), ACTIVE(1), INACTIVE(2);
private final int code;
Status(int code) { this.code = code; }
public int getCode() { return code; }
}
Mistake 2 — Not handling valueOf() exceptions¶
// ❌ crashes on invalid input
String input = request.getParam("status"); // could be "pending" (lowercase)
OrderStatus s = OrderStatus.valueOf(input); // IllegalArgumentException
// ✅ Handle safely
try {
OrderStatus s = OrderStatus.valueOf(input.toUpperCase());
} catch (IllegalArgumentException e) {
// return a proper error response to the client
}
Mistake 3 — Using .equals() instead of ==¶
// ❌ .equals() works, but is redundant and throws NullPointerException if status is null
if (status.equals(OrderStatus.PENDING)) { ... }
// ✅ Use == — safe even if the left side is null when reversed
if (status == OrderStatus.PENDING) { ... }
Mistake 4 — Using String constants in switch instead of Enum¶
// ❌ compiler won't warn when a new case is added but not handled
String status = "PENDING";
if (status.equals("PENDING")) { ... }
else if (status.equals("ACTIVE")) { ... }
// Adding "CANCELLED" later and forgetting to handle it — no warning
// ✅ Enum + exhaustive switch — compiler errors on missing cases
OrderStatus status = OrderStatus.PENDING;
String result = switch (status) {
case PENDING -> "...";
case ACTIVE -> "...";
// Missing CANCELLED → compiler error when switch is an expression
};
11. Interview Questions¶
Q1: Is a Java enum really a class?
Yes. The compiler translates an enum into a class that extends
java.lang.Enum. Each constant is apublic static finalinstance of that class. This is why enums can have fields, constructors, and methods — and why each constant is a real object.
Q2: Why shouldn't you use ordinal() to persist to a database?
ordinal()depends on the declaration order in source code. If someone inserts a new constant in the middle later, all ordinals after it shift — causing old data in the database to map to the wrong constants. The correct approach is to add a dedicatedcodeorvaluefield to the enum and use that field for persistence.
Q3: Can an enum implement an interface? Can it extend a class?
An enum can implement interfaces — this is how you add shared behavior across all constants. An enum cannot extend a class because it already implicitly extends
java.lang.Enum, and Java does not support multiple class inheritance.
Q4: Is Enum thread-safe?
Yes. Enum constants are
static finaland initialized exactly once by the class loader — a process that is thread-safe by the JVM specification. This makes Enum the safest way to implement the Singleton pattern in Java (immune to reflection attacks and serialization issues).
Q5: How does EnumSet differ from a HashSet containing enums?
EnumSetuses a bit-vector internally (one bit per constant, indexed by ordinal), socontains(),add(), andremove()run in O(1) with minimal memory.HashSetis more general but has hashing and boxing overhead. For enum-typed elements, always preferEnumSet.
12. References¶
| Resource | Content |
|---|---|
| JLS §8.9 — Enum Classes | Official specification |
| Oracle Tutorial — Enum Types | Official guide |
| Effective Java — Joshua Bloch | Item 34: Use enums instead of int constants · Item 38: Emulate extensible enums with interfaces |
| Baeldung — Guide to Java Enums | Practical walkthrough |