Enums are a powerful feature in Java, enabling developers to represent fixed sets of constants in a type-safe manner. One lesser-known but sometimes used feature of enums is the ordinal value, which represents the position of an enum constant in its declaration. This article will explore how to use enum ordinals in Java, when to use them, and the best practices you should follow to avoid common bugs and maintenance issues.
🧠 What is an Enum Ordinal in Java?
Every enum constant in Java has an implicit integer value called the ordinal, which is based on its position in the declaration (starting from 0).
Example:
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY;
}
// MONDAY.ordinal() = 0
// FRIDAY.ordinal() = 4
You can retrieve the ordinal using the .ordinal()
method, and you can get the enum constant by ordinal using Day.values()[ordinal]
.
🚀 How to Set an Enum Using Its Ordinal
To retrieve an enum constant from its ordinal:
int ordinal = 2;
Day day = Day.values()[ordinal];
System.out.println(day); // Output: WEDNESDAY
⚠️ Dangers of Using Enum Ordinals
While ordinals are available, relying on them in production code can be risky and brittle:
- Breaking Changes: Changing the order of enum constants will change their ordinal values.
- Serialization Issues: Persisting ordinals (e.g., in a database) can break if the enum is reordered.
- Readability: Ordinal values are not self-descriptive, making code harder to understand.
- Exceptions: Accessing an invalid ordinal index will throw
ArrayIndexOutOfBoundsException
.
✅ Best Practices When Working with Enums and Ordinals
1. Prefer Enum Name Over Ordinal
Instead of saving the ordinal to a database, save the name (Enum.name()
) and retrieve it using Enum.valueOf()
:
String saved = "MONDAY";
Day day = Day.valueOf(saved);
2. Avoid Using Ordinal in Public Interfaces
Never expose .ordinal()
to clients or external APIs—always use a more descriptive and stable representation.
3. Use Custom Fields if You Need Stable Values
If you need an enum constant to map to a stable value (like an ID), define a custom field:
public enum Status {
PENDING(1),
APPROVED(2),
REJECTED(3);
private final int code;
Status(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static Status fromCode(int code) {
for (Status s : values()) {
if (s.code == code) return s;
}
throw new IllegalArgumentException("Unknown code: " + code);
}
}
4. Use EnumSet
and EnumMap
for Performance
When you need sets or maps of enums, use EnumSet
and EnumMap
for optimal performance and clarity.
💾 Enum Ordinals and Persistence
If you use JPA or another ORM, beware that enum values are often stored by ordinal by default. This can be dangerous if the enum order changes.
✅ Best Practice:
Use @Enumerated(EnumType.STRING)
to store the enum name, not ordinal.
@Enumerated(EnumType.STRING)
private Day day;
🧪 Unit Test Enums When Used Dynamically
If you rely on dynamic construction of enums via ordinals or names, add unit tests to cover changes and prevent future breakage:
@Test
void testEnumOrdinalMapping() {
assertEquals(Day.MONDAY, Day.values()[0]);
assertEquals("MONDAY", Day.MONDAY.name());
}
🔚 Conclusion
While Java enum ordinals can be useful in controlled environments, using them in production code requires caution. Prefer more stable representations like enum names or custom codes to ensure your application remains maintainable, readable, and resilient to changes.
🚀 Key Takeaways:
- Avoid using
.ordinal()
for persistence or APIs. - Use
Enum.name()
andEnum.valueOf()
for better safety and readability. - Define custom fields when stable mapping is required.
- Use
EnumSet
andEnumMap
for efficient enum collections. - Use
@Enumerated(EnumType.STRING)
in JPA to avoid data integrity issues.