Of course! This is a common and powerful pattern in Java. You can't have a literal enum of enum because an enum is a special type, not a class you can nest in that way.

However, the most idiomatic and effective way to achieve this is to have an enum that holds another enum as a field. This creates a structured, type-safe, and readable hierarchy.
Let's break it down with a clear example: representing a Pizza with a Size and a Topping.
The Problem: Representing Hierarchical Data
Imagine you want to model this:
- A
Pizzahas aSize(Small, Medium, Large). - A
Pizzaalso has aTopping(Pepperoni, Mushroom, Olives).
You could use two separate enums, but they are related. A Pizza object needs to hold one of each. This is where the "enum of enum" pattern shines.

Solution 1: The Standard Approach (Enum with an Enum Field)
This is the most common and recommended approach. We'll create two enums: PizzaSize and PizzaTopping. Then, we'll create a Pizza enum that uses these two as its properties.
Step 1: Define the "Inner" Enums (The Categories)
First, define the enums for the categories you want to group.
PizzaSize.java
public enum PizzaSize {
SMALL("10 inches"),
MEDIUM("12 inches"),
LARGE("14 inches");
private final String description;
// Constructor for the enum constant
PizzaSize(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
PizzaTopping.java

public enum PizzaTopping {
PEPPERONI,
MUSHROOM,
OLIVES,
EXTRA_CHEESE;
}
Step 2: Define the "Outer" Enum (The Main Object)
Now, create the main enum (Pizza) that will hold instances of the other enums as fields.
Pizza.java
public enum Pizza {
// Enum constants that define the specific pizza types
MARGHERITA(PizzaSize.MEDIUM, PizzaTopping.OLIVES),
PEPPERONI_LOVERS(PizzaSize.LARGE, PizzaTopping.PEPPERONI),
VEGGIE_DELIGHT(PizzaSize.SMALL, PizzaTopping.MUSHROOM, PizzaTopping.OLIVES);
// Fields to hold the properties of each pizza type
private final PizzaSize size;
private final PizzaTopping[] toppings; // Use an array to allow multiple toppings
// Constructor for the enum constant
Pizza(PizzaSize size, PizzaTopping... toppings) { // Use varargs for flexibility
this.size = size;
this.toppings = toppings;
}
// --- Getters ---
public PizzaSize getSize() {
return size;
}
public PizzaTopping[] getToppings() {
return toppings; // Return a copy for immutability if needed
}
// --- Helper Methods ---
public String getDescription() {
StringBuilder sb = new StringBuilder();
sb.append("A ").append(size).append(" pizza with ");
if (toppings.length == 0) {
sb.append("no toppings.");
} else {
for (int i = 0; i < toppings.length; i++) {
sb.append(toppings[i]);
if (i < toppings.length - 1) {
sb.append(", ");
}
}
}
return sb.toString();
}
}
Step 3: How to Use It
Now you can use your structured enum in a type-safe and readable way.
Main.java
public class Main {
public static void main(String[] args) {
// Get a specific pizza type from the enum
Pizza myPizza = Pizza.PEPPERONI_LOVERS;
// Access its properties
System.out.println("I ordered a: " + myPizza);
System.out.println("Size: " + myPizza.getSize());
System.out.println("Size Description: " + myPizza.getSize().getDescription());
System.out.println("Toppings: " + String.join(", ", myPizza.getToppings().toString())); // A bit of a hack for printing array
// Use the helper method
System.out.println("\n--- Full Description ---");
System.out.println(myPizza.getDescription());
System.out.println("\n--- All Available Pizzas ---");
for (Pizza pizza : Pizza.values()) {
System.out.println(pizza.getDescription());
}
}
}
Output:
I ordered a: PEPPERONI_LOVERS
Size: LARGE
Size Description: 14 inches
Toppings: [Ljava.lang.Enum;@15db9742
--- Full Description ---
A LARGE pizza with PEPPERONI
--- All Available Pizzas ---
A MEDIUM pizza with OLIVES
A LARGE pizza with PEPPERONI
A SMALL pizza with MUSHROOM, OLIVES
Solution 2: Nested Enums (If They Are Tightly Coupled)
Sometimes, one enum is exclusively used by another. In this case, you can nest it inside the outer enum to show the relationship and encapsulate it.
Let's refactor the Pizza example to use a nested Topping enum.
PizzaWithNestedEnum.java
public enum PizzaWithNestedEnum {
// Enum constants
MARGHERITA(Medium, Olive),
PEPPERONI_LOVERS(Large, Pepperoni),
VEGGIE_DELIGHT(Small, Mushroom, Olive);
// The nested enum for Toppings
public enum Topping {
PEPPERONI, MUSHROOM, OLIVE, EXTRA_CHEESE
}
// The nested enum for Sizes
public enum Size {
Small("10 inches"), Medium("12 inches"), Large("14 inches");
private final String description;
Size(String description) {
this.description = description;
}
public String getDescription() { return description; }
}
// Fields of the main enum
private final Size size;
private final Topping[] toppings;
// Constructor
PizzaWithNestedEnum(Size size, Topping... toppings) {
this.size = size;
this.toppings = toppings;
}
// Getters
public Size getSize() { return size; }
public Topping[] getToppings() { return toppings; }
public String getDescription() {
return "A " + size + " pizza with " + String.join(", ", toppings.toString());
}
}
Usage: The usage is slightly different as you now reference the nested enum through the outer one.
public class Main {
public static void main(String[] args) {
PizzaWithNestedEnum myPizza = PizzaWithNestedEnum.PEPPERONI_LOVERS;
// Access the nested enum type
PizzaWithNestedEnum.Size size = myPizza.getSize();
System.out.println("Pizza Size: " + size);
System.out.println("Size Description: " + size.getDescription()); // Access method on nested enum
// Access the nested Topping enum
PizzaWithNestedEnum.Topping[] toppings = myPizza.getToppings();
System.out.println("Topping Type: " + toppings[0].getClass().getEnclosingClass()); // Shows it's the nested Topping
}
}
Comparison of Approaches
| Feature | Solution 1: Separate Enums | Solution 2: Nested Enums |
|---|---|---|
| Encapsulation | Lower. The enums are global. | Higher. The nested enum is only visible to its parent. |
| Readability | Can be cleaner if the enums are used in other contexts. | Can be very clear if the enums are only used together. Shows strong coupling. |
| Namespace | Each enum has its own namespace (PizzaSize.MEDIUM). |
Nested enums share the namespace of the outer enum (PizzaWithNestedEnum.Medium). |
| Use Case | Best for enums that might be used independently or in multiple places. | Best for enums that are conceptually part of a larger whole and have no other purpose. |
Conclusion
While you can't literally write enum MyOuterEnum { INNER_ENUM.VALUE }, the pattern of an enum holding another enum as a field is the standard, powerful, and type-safe way to achieve the desired "enum of enum" functionality in Java.
For most use cases, Solution 1 (separate enums) is preferred for its flexibility and cleaner separation of concerns. Use Solution 2 (nested enums) when the relationship is very tight and you want to enforce that one enum is only ever used by the other.
