A Comprehensive Guide to Java Enums: Basics, Advanced Usage, and Design Patterns
This article explains Java enums introduced in Java 5, shows how to define simple and complex enum types, demonstrates their use in comparisons, switch statements, collections like EnumSet and EnumMap, and illustrates applying enums to design patterns such as Singleton and Strategy, while providing practical code examples and JSON serialization techniques.
Java enums, introduced in Java 5, are a special kind of class that always extend java.lang.Enum and are often used to replace constant values, offering better readability and compile‑time safety.
A basic example defines an enum PizzaStatus with the constants ORDERED , READY and DELIVERED :
package shuang.kou.enumdemo.enumtest;
public enum PizzaStatus {
ORDERED,
READY,
DELIVERED;
}Printing an enum value shows its name and class:
System.out.println(PizzaStatus.ORDERED.name()); // ORDERED
System.out.println(PizzaStatus.ORDERED); // ORDERED
System.out.println(PizzaStatus.ORDERED.name().getClass()); // class java.lang.String
System.out.println(PizzaStatus.ORDERED.getClass()); // class shuang.kou.enumdemo.enumtest.PizzaStatusEnums can contain custom methods. For instance, a Pizza class can hold a PizzaStatus field and provide an isDeliverable() method that checks whether the status is READY :
public class Pizza {
private PizzaStatus status;
public boolean isDeliverable() {
return getStatus() == PizzaStatus.READY;
}
}Because each enum constant is a singleton, the == operator can safely compare enum values, providing both compile‑time and runtime safety. Using == avoids NullPointerException when both operands are null , unlike equals() .
Enums work naturally in switch statements:
public int getDeliveryTimeInDays() {
switch (status) {
case ORDERED: return 5;
case READY: return 2;
case DELIVERED: return 0;
}
return 0;
}Enums may also define fields, constructors, and overridden methods. The following example adds a timeToDelivery field and overrides methods for each constant:
public enum PizzaStatus {
ORDERED(5) {
@Override public boolean isOrdered() { return true; }
},
READY(2) {
@Override public boolean isReady() { return true; }
},
DELIVERED(0) {
@Override public boolean isDelivered() { return true; }
};
private int timeToDelivery;
PizzaStatus(int time) { this.timeToDelivery = time; }
public int getTimeToDelivery() { return timeToDelivery; }
public boolean isOrdered() { return false; }
public boolean isReady() { return false; }
public boolean isDelivered() { return false; }
}For efficient handling of enum collections, Java provides EnumSet and EnumMap . EnumSet is a compact, type‑safe set implementation, while EnumMap is a high‑performance map keyed by enum constants. Example usage:
private static EnumSet<PizzaStatus> undeliveredPizzaStatuses =
EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);
public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) {
return input.stream()
.filter(p -> !undeliveredPizzaStatuses.contains(p.getStatus()))
.collect(Collectors.toList());
}
public static EnumMap<PizzaStatus, List<Pizza>> groupPizzaByStatus(List<Pizza> list) {
return list.stream().collect(Collectors.groupingBy(
Pizza::getStatus,
() -> new EnumMap<>(PizzaStatus.class),
Collectors.toList()
));
}Enums can implement design patterns. The Singleton pattern can be expressed with a single‑element enum, guaranteeing a single instance and built‑in serialization safety:
public enum PizzaDeliverySystemConfiguration {
INSTANCE;
private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL;
public static PizzaDeliverySystemConfiguration getInstance() { return INSTANCE; }
public PizzaDeliveryStrategy getDeliveryStrategy() { return deliveryStrategy; }
}The Strategy pattern can also be modelled with an enum where each constant provides its own implementation:
public enum PizzaDeliveryStrategy {
EXPRESS {
@Override public void deliver(Pizza p) {
System.out.println("Pizza will be delivered in express mode");
}
},
NORMAL {
@Override public void deliver(Pizza p) {
System.out.println("Pizza will be delivered in normal mode");
}
};
public abstract void deliver(Pizza p);
}With Java 8, the same logic can be written more concisely using streams and lambda expressions, as shown in the simplified getAllUndeliveredPizzas and groupPizzaByStatus methods above.
Jackson can serialize enums as JSON objects by using the @JsonFormat(shape = JsonFormat.Shape.OBJECT) annotation, allowing fields such as timeToDelivery and custom boolean flags to appear in the output:
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum PizzaStatus {
ORDERED(5) { @Override public boolean isOrdered() { return true; } },
READY(2) { @Override public boolean isReady() { return true; } },
DELIVERED(0){ @Override public boolean isDelivered(){ return true; } };
private int timeToDelivery;
// getters, constructor, etc.
}Finally, the article presents a practical enum PinType used for SMS verification codes, demonstrating how enums can carry additional data (code and message) and provide utility methods.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.