[J2SE 5.0专题][2.4]类型安全的枚举
在细述J2SE 5.0中引入的类型安全枚举的用法之前,我想先简单介绍一下这一话题的背景。
我们知道,在C中,我们可以定义枚举类型来使用别名代替一个集合中的不同元素,通常是用于描述那些可以归为一类,而又有限数量的类别或者概念,如一周的每一天、月份、颜色、扑克牌、太阳系的行星、五大洲、四大洋、季节、学科、运算符,等等。它们通常看上去是这个样子:
typedef enum {SPRING, SUMMER, AUTUMN, WINTER} season;
实质上,这些别名被处理成int常量,比如0代表SPRING,1代表SUMMER,以此类推。因为这些别名最终就是int,于是你可以对它们进行四则运算,这就造成了语意上的不明确。另外,我们可以想象在这个枚举之后,我们又定义了一个
typedef enum {GREEN, HOLMES, RUSSELL, LEE, SUMMER} name_of_teacher;
因为C中的enum没有提供名字空间的区分,所以上面的两个enum就存在着冲突。
Java 一开始并没有考虑引入枚举的概念,也许是出于维护Java语言简洁的考虑,但是时过境迁,对于枚举的需求并没有因为Java本身没有提供而消失,于是出现 了一些常见的Java中的枚举设计模式,如int enum和typesafe enum,还有不少开源的枚举API和不开源的内部实现。
我大致说一下int enum模式和typesafe enum模式。所谓int enum模式就是模仿C中对enum的实现,如:
public class Season {
public static final int SPRING = 0;
public static final int SUMMER = 1;
public static final int AUTUMN = 2;
public static final int WINTER = 3;
}
这种模式跟C中的枚举没有太多本质上的区别,C枚举有的问题它基本上也有。而typesafe enum模式则要显得健壮得多:
public class Season {
private final String name;
private Season(String name) {
this.name = name;
}
public String toString() {
return name;
}
public static final Season SPRING = new Season("spring");
public static final Season SUMMER = new Season("summer");
public static final Season AUTUMN = new Season("autumn");
public static final Season WINTER = new Season("winter");
}
后一种实现首先通过私有的构造方法阻止了对该类的继承和显式实例化,因而我们只可能取得定义好的四种Season类别,并且提供了方便的 toString()方法获取有意义的说明,而且由于这是一个完全意义上的类,所以我们可以很方便的加入自己的方法和逻辑来自定义我们的枚举类。不过自定 义的枚举类也显然存在着不足:比如有太多的功能需要我们自己去实现,而且随着代码行数的增加,出错的可能性也在增加,我们也很难保证我们自定义的这些逻辑 可以满足所有可能的情况;还有就是自定义的枚举类不能用于switch语句。
最终,Java决定拥抱枚举,在J2SE 5.0中,我们看到了这一变化。用一个实际的例子来说(借用Java官网上的例子,懒得想了):
public enum Operation {
PLUS { double eval(double x, double y) { return x + y; } },
MINUS { double eval(double x, double y) { return x - y; } },
TIMES { double eval(double x, double y) { return x * y; } },
DIVIDE { double eval(double x, double y) { return x / y; } };
// Do arithmetic op represented by this constant
abstract double eval(double x, double y);
}
public static void main(String args[]) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
for (Operation op : Operation.values()) {
System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y));
}
}
怎么样,使用枚举,我们能够很方便的实现一些原先比较麻烦和费事的功能了吧?有几点注意一下,那就是:
1- J2SE 5.0中的枚举中,所有项都自动默认为static(很显然这是必要的)
2- 对于每一个枚举类,都可以调用公用的方法,如values() - 返回一个包含了所有枚举项的数组
3- 对于每个枚举项,也能够调用基本的对象方法,如toString()
4- 从某种意义上讲,enum关键字其实就是代表了一种新的类型的class,因为从本质上看,它就是一个类
另外,在 java.util包中新增了两个类用于更方便的处理枚举类型,这两个类分别是 EnumSet和 EnumMap。
有关类型安全的枚举更详细的信息,参考 这里。