A Comprehensive Guide to Java Enums: Basics, Advanced Techniques, and Design Patterns
This article explains what Java enums are, why they are preferable to constants, demonstrates basic and custom enum definitions, shows how to compare, switch, and extend enums with properties, methods and constructors, and explores advanced uses such as EnumSet, EnumMap, Singleton and Strategy patterns, including Java 8 stream examples and JSON serialization.
Java enums, introduced in Java 5, are a special kind of class that implicitly extends java.lang.Enum . They provide type‑safe constants, enable compile‑time checking, and improve code readability compared with plain integer or string constants.
Basic enum example defining pizza order statuses:
package shuang.kou.enumdemo.enumtest;
public enum PizzaStatus {
ORDERED,
READY,
DELIVERED;
}Printing enum values demonstrates that name() returns a String while the enum itself is an instance of the enum type.
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.PizzaStatusCustom enum methods – an enum can contain fields, constructors and additional methods. The following class shows a Pizza with an inner enum that defines a method isDeliverable() based on the status.
public class Pizza {
private PizzaStatus status;
public enum PizzaStatus {
ORDERED,
READY,
DELIVERED;
}
public boolean isDeliverable() {
if (getStatus() == PizzaStatus.READY) {
return true;
}
return false;
}
// getters and setters omitted for brevity
}Comparing enums – because each enum constant is a singleton, the == operator can be safely used for both compile‑time and runtime checks, avoiding NullPointerException that may arise with equals() on a null reference.
if (testPz.getStatus() == Pizza.PizzaStatus.DELIVERED) { /* safe */ }Using enums in switch statements provides clear branching based on the constant values.
public int getDeliveryTimeInDays() {
switch (status) {
case ORDERED: return 5;
case READY: return 2;
case DELIVERED: return 0;
}
return 0;
}Enums with fields, methods and constructors can store extra data, such as a delivery time, and override behavior per 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;
public int getTimeToDelivery() { return timeToDelivery; }
PizzaStatus(int time) { this.timeToDelivery = time; }
public boolean isOrdered() { return false; }
public boolean isReady() { return false; }
public boolean isDelivered() { return false; }
}EnumSet and EnumMap are specialized collections for enums. EnumSet offers a compact, type‑safe set implementation, while EnumMap provides an efficient map keyed by enum constants.
// Example of EnumSet
private static EnumSet
undeliveredPizzaStatuses =
EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);
// Example of EnumMap grouping
public static EnumMap
> groupPizzaByStatus(List
list) {
return list.stream().collect(
Collectors.groupingBy(Pizza::getStatus,
() -> new EnumMap<>(PizzaStatus.class),
Collectors.toList()));
}Design patterns with enums – enums can implement the Singleton pattern (single‑element enum) and the Strategy pattern by defining abstract methods that each constant overrides.
// Singleton via enum
public enum PizzaDeliverySystemConfiguration {
INSTANCE;
private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL;
public static PizzaDeliverySystemConfiguration getInstance() { return INSTANCE; }
public PizzaDeliveryStrategy getDeliveryStrategy() { return deliveryStrategy; }
}
// Strategy via enum
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);
}Java 8 integration – using streams and lambda expressions makes operations on collections of enums concise, e.g., filtering undelivered pizzas or grouping by status.
public static List
getAllUndeliveredPizzas(List
input) {
return input.stream()
.filter(p -> !deliveredPizzaStatuses.contains(p.getStatus()))
.collect(Collectors.toList());
}JSON serialization – with Jackson annotations, an enum can be serialized as a JSON object exposing its fields.
@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;
@JsonProperty("timeToDelivery")
public int getTimeToDelivery() { return timeToDelivery; }
// constructors, getters, and default methods omitted
}The article concludes that Java enums are a powerful language feature that improve type safety, enable clean design patterns, and integrate well with modern Java APIs.
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.