枚举类型无法被继承,这一点可能有时会让人沮丧。想要继承枚举的动机,一部分源自希望扩充原始枚举中的元素,另一部分源自想要使用子类型来创建不同的子分组。
你可以在一个接口内对元素进行分组,然后基于这个接口生成一个枚举,通过这样的方式来实现元素的分类。举个例子,假如你有一些类型互不相同的 food (食物),想创建若干 enum 来组织它们,但又希望它们仍然是 Food 类型,你可以这么做:
Food.java
public interface Food {
enum Appetizer implements Food {
SALAD, SOUP, SPRING_ROLLS;
}
enum MainCourse implements Food {
LASAGNE, BURRITO, PAD_THAI,
LENTILS, HUMMUS, 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;
}
}
实现接口是唯一可子类化枚举的方式,因此所有嵌套在 Food 中的枚举类型都实现了 Food 接口。现在我们基本可以说“ 一切都是某种类型的Food”,如下例所示:
TypeOfFood.JAVA
import enums.TEST0522.Food.*;
public class TypeOfFood {
public static void main(String[] args) {
Food food = Appetizer.SALAD;
food = MainCourse.LASAGNE;
food = Dessert.GELATO;
food = Coffee.CAPPUCCINO;
}
}
对于每个实现了 Food 接口的枚举类型,都可以向上转型为 Food ,因此它们全都是 Food 类型 。
但是,当你要处理一组类型时,接口往往就不如枚举有用。如果要创建“由枚举组成的枚举”,你可以为 Food 中的每个枚举类型都创建一个外部枚挙类型:
Course.java
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);
}
}
上面的代码中,每个枚举类型都接收相应的 Class 对象以作为构造参数,从而可以使用 getEnumConstants() 来提取出所有的枚举实例并存储起来,这些实例稍后会在 randomselection() 中被用到。好了,现在我们可以从每个 Course (菜项,如“ 前菜/主菜/ 甜点”)中选择一种 Food (食物),生成一份随机配好的午餐了。
Meal.java
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("***");
}
}
}
运行结果如下:
此处,创建一个由枚举类型组成的枚举类型的意义在于,可以方便地遍历每个 Course。在稍后的例子 VendingMachine.java 中,你会看到另一种由不同约束条件指定的分类方法。
另一种更简洁的分类方法是在枚举内嵌套枚举,如下例所示:
SecurityCategory.java
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());
}
}
}
运行结果如下:
Security接口用于将内部的枚举类型作为公共类型聚合到一起,然后再将它们归类到
SecurityCategory 中的枚举中。
如果将此方法应用到前面的Food示例,那么结果如下:
Meal2.java
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, HUMMUS, 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("***");
}
}
}
运行结果如下:
最后,虽然这只是对代码重新组织了一下,但在某些情况下,这样可以使结构更加清晰。