Java枚举类型

     关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用。这些具名的值称为枚举值,这种新的类型称为枚举类型。

     下面是一个简单的表示星期几的枚举:

1 public enum Day {
2     SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
3 }

     在创建enum时,编译器会自动添加一些有用的特性。比如创建toString()方法以便显示某个enum实例的名字;创建ordinal()方法表示某个特定enum常量的申明顺序;values()方法用来按照enum常量的申明顺序产生这些常量构成的数组。enum看起来像是一种新的数据类型,除了编译上面这些特殊的编译行为,很大程度上可以将enum看成是一个普通的类来处理。这些内容在后面会有详细的介绍。

public static void main(String[] args) {
    System.out.println(Day.class.getSuperclass());
    for (Day day : Day.values()) {
        System.out.println(day.name() + " ordinal: " + day.ordinal());
    }
}

class java.lang.Enum

SUNDAY ordinal: 0

MONDAY ordinal: 1

TUESDAY ordinal: 2

WEDNESDAY ordinal: 3

THURSDAY ordinal: 4

FRIDAY ordinal: 5

SATURDAY ordinal: 6

     上面的代码中输出了枚举类型Day的父类及演示了values()、name()、ordinal()三个方法的调用。从输出结果中可以看到Day的父类是java.lang.Enum,但是在定义Day的时候并没有通过extends指明继承java.lang.Enum,也不需要通过extends关键字指定。当创建enum时编译器会生成一个相关的类,这个类会继承java.lang.Enum。既然枚举实例已经集成了java.lang.Enum,Java又不支持多继承,所以enum不能再继承其他的类,但是能实现接口。ordinal()方法返回一个int值,这是每个enum实例在申明时的次序,从0开始。

     除了不能继承自一个enum外,基本上可以将enum看做一个常规的类。也就是说可以向enum中添加方法,比如返回enum自身描述的方法,还可以添加main方法。下面是一个演示enum添加自定义方法和实现接口的例子。

 1 public enum Signal implements ObjectDescription {
 2     Red("红灯", "敢过去就是6分,还要罚款哦"), 
 3     Yellow("黄灯", "黄灯你也不敢过"), 
 4     Green("绿灯", "绿灯也得小心过啊");
 5 
 6     private String name;
 7     private String description;
 8 
 9     private Signal(String name, String description) {
10         this.name = name;
11         this.description = description;
12     }
13 
14     private String getName() {
15         return name;
16     }
17 
18     private String getDescription() {
19         return description;
20     }
21 
22     @Override
23     public String todo() {
24         return "Signal类用于表示交通信号指示灯," + this + "用于表示" + this.getName();
25     }
26 
27     public static void main(String[] args) {
28         for (Signal signal : Signal.values()) {
29             System.out.println(signal.todo());
30         }
31         for (Signal signal : Signal.values()) {
32             System.out.println(signal.getName() + ": "
33                     + signal.getDescription());
34         }
35     }
36 
37 }

     注意:如果要自定义方法,那么必须在enum实例序列的最后添加一个分号。同时,Java要求必须先定义enum实例,否则编译时会报错。

     enum的构造器无论是不是private,都只能在enum定义的内部使用来创建enum实例,一旦enum的定义结束,编译器就不允许再使用其构造器来创建任何实例了。

 

使用接口组织枚举

     无法使用继承限制了枚举的使用,比如需要用enum表示食物,但同时必须分为水果,点心等类型的时候就没那么方便的满足了。

     下面通过在一个接口内部创建实现该接口的枚举,从而达到对元素进行分类组织的目的。

 1 public interface Food {
 2     enum Appetizer implements Food {
 3         SALAD, SOUP, SPRING_ROLLS;
 4     }
 5 
 6     enum MainCourse implements Food {
 7         LASAGNE, BURRITO, PAD_THAI, LENTILS, HUMMOUS, VINDALOO;
 8     }
 9 
10     enum Dessert implements Food {
11         TIRAMISU, GELATO, BLACK_fOREST_CAKE, FRUIT;
12     }
13 
14     enum Coffee implements Food {
15         BLACK_COFFEE, DECAF_COFFEE, LATTE;
16     }
17 }

     对于enum而言,实现接口是使其子类化的唯一办法。通过上面的形式,成功的对不同的食物进行分组,但都是Food。 

 

枚举的枚举

     下面是一个枚举的随机选择器,是一个工具类。

 1 public class Enums {
 2     private static Random rand = new Random(47);
 3 
 4     public static <T extends Enum<T>> T randrom(Class<T> ec) {
 5         return random(ec.getEnumConstants());
 6     }
 7 
 8     public static <T> T random(T[] values) {
 9         return values[rand.nextInt(values.length)];
10     }
11 }

     结合工具类及上面Food接口的内容,下面使用枚举的枚举实现一个产生随机菜单的例子。

 1 public enum Course {
 2     APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(
 3             Food.Dessert.class), COFFEE(Food.Coffee.class);
 4     private Food[] values;
 5 
 6     private Course(Class<? extends Food> kind) {
 7         // 返回枚举中所有的元素,及所有实例构成的数组,如果kind不是枚举返回null
 8         values = kind.getEnumConstants();
 9     }
10 
11     public Food randomSelection() {
12         return Enums.random(values);
13     }
14 
15     public static void main(String[] args) {
16         // 产生5份随机菜单
17         for (int i = 0; i < 5; i++) {
18             for (Course c : Course.values()) {
19                 Food food = c.randomSelection();
20                 System.out.println(food);
21             }
22             System.out.println("--------------------");
23         }
24     }
25 }

     下面给出一个验证values()方法不是通过父类继承的方法。

 1 public enum Signal implements ObjectDescription {
 2     Red("红灯", "敢过去就是6分,还要罚款哦"), Yellow("黄灯", "黄灯你也不敢过"), Green("绿灯", "绿灯也得小心过啊");
 3 
 4     private String name;
 5     private String description;
 6 
 7     private Signal(String name, String description) {
 8         this.name = name;
 9         this.description = description;
10     }
11 
12     private String getName() {
13         return name;
14     }
15 
16     private String getDescription() {
17         return description;
18     }
19 
20     @Override
21     public String todo() {
22         return "Signal类用于表示交通信号指示灯," + this + "用于表示" + this.getName();
23     }
24 
25     public static void main(String[] args) {
26         Set<Method> methodSet = new HashSet<Method>();
27         Method[] signalMethods = Signal.class.getMethods();
28         for (Method m : signalMethods) {
29             methodSet.add(m);
30         }
31         Method[] superClassMethods = Signal.class.getSuperclass().getMethods();
32         for (Method m : superClassMethods) {
33             methodSet.remove(m);
34         }
35         System.out.println(methodSet);
36     }
37 
38 }

 

posted @ 2013-01-11 20:18  杭州.Mark  阅读(4350)  评论(0编辑  收藏  举报