杰瑞科技汇

Java中枚举嵌套枚举如何实现?

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.

Java中枚举嵌套枚举如何实现?-图1
(图片来源网络,侵删)

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 Pizza has a Size (Small, Medium, Large).
  • A Pizza also has a Topping (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.

Java中枚举嵌套枚举如何实现?-图2
(图片来源网络,侵删)

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

Java中枚举嵌套枚举如何实现?-图3
(图片来源网络,侵删)
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.

分享:
扫描分享到社交APP
上一篇
下一篇