企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
<!-- Using Interfaces for Organization --> ## 使用接口组织枚举 无法从 enum 继承子类有时很令人沮丧。这种需求有时源自我们希望扩展原 enum 中的元素,有时是因为我们希望使用子类将一个 enum 中的元素进行分组。 在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的。举例来说,假设你想用 enum 来表示不同类别的食物,同时还希望每个 enum 元素仍然保持 Food 类型。那可以这样实现: ```java // enums/menu/Food.java // Subcategorization of enums within interfaces package enums.menu; public interface Food { enum Appetizer implements Food { SALAD, SOUP, SPRING_ROLLS; } enum MainCourse implements Food { LASAGNE, BURRITO, PAD_THAI, LENTILS, HUMMOUS, VINDALOO; } enum Dessert implements Food { TIRAMISU, GELATO, BLACK_FOREST_CAKE, FRUIT, CREME_CARAMEL; } enum Coffee implements Food { BLACK_COFFEE, DECAF_COFFEE, ESPRESSO, LATTE, CAPPUCCINO, TEA, HERB_TEA; } } ``` 对于 enum 而言,实现接口是使其子类化的唯一办法,所以嵌入在 Food 中的每个 enum 都实现了 Food 接口。现在,在下面的程序中,我们可以说“所有东西都是某种类型的 Food"。 ```java // enums/menu/TypeOfFood.java // {java enums.menu.TypeOfFood} package enums.menu; import static enums.menu.Food.*; public class TypeOfFood { public static void main(String[] args) { Food food = Appetizer.SALAD; food = MainCourse.LASAGNE; food = Dessert.GELATO; food = Coffee.CAPPUCCINO; } } ``` 如果 enum 类型实现了 Food 接口,那么我们就可以将其实例向上转型为 Food,所以上例中的所有东西都是 Food。 然而,当你需要与一大堆类型打交道时,接口就不如 enum 好用了。例如,如果你想创建一个“枚举的枚举”,那么可以创建一个新的 enum,然后用其实例包装 Food 中的每一个 enum 类: ```java // enums/menu/Course.java package enums.menu; import onjava.*; public enum Course { APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(Food.Dessert.class), COFFEE(Food.Coffee.class); private Food[] values; private Course(Class<? extends Food> kind) { values = kind.getEnumConstants(); } public Food randomSelection() { return Enums.random(values); } } ``` 每一个 Course 的实例都将其对应的 Class 对象作为构造器的参数。通过 getEnumConstants0 方法,可以从该 Class 对象中取得某个 Food 子类的所有 enum 实例。这些实例在 randomSelection() 中被用到。因此,通过从每一个 Course 实例中随机地选择一个 Food,我们便能够生成一份菜单: ```java // enums/menu/Meal.java // {java enums.menu.Meal} package enums.menu; public class Meal { public static void main(String[] args) { for(int i = 0; i < 5; i++) { for(Course course : Course.values()) { Food food = course.randomSelection(); System.out.println(food); } System.out.println("***"); } } } ``` 输出为: ``` SPRING_ROLLS VINDALOO FRUIT DECAF_COFFEE *** SOUP VINDALOO FRUIT TEA *** SALAD BURRITO FRUIT TEA *** SALAD BURRITO CREME_CARAMEL LATTE *** SOUP BURRITO TIRAMISU ESPRESSO *** ``` 在这个例子中,我们通过遍历每一个 Course 实例来获得“枚举的枚举”的值。稍后,在 VendingMachine.java 中,我们会看到另一种组织枚举实例的方式,但其也有一些其他的限制。 此外,还有一种更简洁的管理枚举的办法,就是将一个 enum 嵌套在另一个 enum 内。就像这样: ```java // enums/SecurityCategory.java // More succinct subcategorization of enums import onjava.*; enum SecurityCategory { STOCK(Security.Stock.class), BOND(Security.Bond.class); Security[] values; SecurityCategory(Class<? extends Security> kind) { values = kind.getEnumConstants(); } interface Security { enum Stock implements Security { SHORT, LONG, MARGIN } enum Bond implements Security { MUNICIPAL, JUNK } } public Security randomSelection() { return Enums.random(values); } public static void main(String[] args) { for(int i = 0; i < 10; i++) { SecurityCategory category = Enums.random(SecurityCategory.class); System.out.println(category + ": " + category.randomSelection()); } } } ``` 输出为: ``` BOND: MUNICIPAL BOND: MUNICIPAL STOCK: MARGIN STOCK: MARGIN BOND: JUNK STOCK: SHORT STOCK: LONG STOCK: LONG BOND: MUNICIPAL BOND: JUNK ``` Security 接口的作用是将其所包含的 enum 组合成一个公共类型,这一点是有必要的。然后,SecurityCategory 才能将 Security 中的 enum 作为其构造器的参数使用,以起到组织的效果。 如果我们将这种方式应用于 Food 的例子,结果应该这样: ```java // enums/menu/Meal2.java // {java enums.menu.Meal2} package enums.menu; import onjava.*; public enum Meal2 { APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(Food.Dessert.class), COFFEE(Food.Coffee.class); private Food[] values; private Meal2(Class<? extends Food> kind) { values = kind.getEnumConstants(); } public interface Food { enum Appetizer implements Food { SALAD, SOUP, SPRING_ROLLS; } enum MainCourse implements Food { LASAGNE, BURRITO, PAD_THAI, LENTILS, HUMMOUS, VINDALOO; } enum Dessert implements Food { TIRAMISU, GELATO, BLACK_FOREST_CAKE, FRUIT, CREME_CARAMEL; } enum Coffee implements Food { BLACK_COFFEE, DECAF_COFFEE, ESPRESSO, LATTE, CAPPUCCINO, TEA, HERB_TEA; } } public Food randomSelection() { return Enums.random(values); } public static void main(String[] args) { for(int i = 0; i < 5; i++) { for(Meal2 meal : Meal2.values()) { Food food = meal.randomSelection(); System.out.println(food); } System.out.println("***"); } } } ``` 输出为: ``` SPRING_ROLLS VINDALOO FRUIT DECAF_COFFEE *** SOUP VINDALOO FRUIT TEA *** SALAD BURRITO FRUIT TEA *** SALAD BURRITO CREME_CARAMEL LATTE *** SOUP BURRITO TIRAMISU ESPRESSO *** ``` 其实,这仅仅是重新组织了一下代码,不过多数情况下,这种方式使你的代码具有更清晰的结构。