
3.11 枚举(enum)

扫码看视频
3.11.1 原始的枚举实现
在Java 5之前,没有枚举类型。要想模拟一个枚举类型,可以使用静态常量。

要使用这些模拟的枚举值时,直接用Week类名调用对应的静态常量就可以了。

不过这种方式并不保险,当声明一个方法接受int类型的参数时,我们也可以使用Week的枚举值作为参数传入这个方法。

编译器并不会报错,但是这种代码会让人疑惑:这个方法到底接收的是什么参数?
3.11.2 枚举类型
Java 5新增了枚举数据类型,这使得代码更为安全,如果Week是真正的枚举类型,那么上面调用subtract方法的代码将无法通过编译。
定义枚举类型需要使用enum关键字,我们看代码3.18。

上述代码定义了一个枚举类型Week,用该类型声明的变量只能存储枚举类型定义中给出的某个枚举值,或者null值。
在定义好枚举类型后,如何使用呢?代码3.19给出了一种用法。


可以用switch语句很方便地判断枚举的值。
如果我们使用JDK自带的javap工具来反编译Week.class文件,就会发现输出的结果为:

从上述反编译后的代码我们可以看出,实际上枚举类型还是一个类,枚举值还是静态常量,只不过这些静态常量的类型就是该类本身。
从反编译的代码中可以看到编译器给Week类增加了两个静态方法values和valueOf,前者可以得到所有的枚举常量,后者可以通过传入一个枚举常量的名字字符串来得到枚举常量,例如:

输出结果为:

与C++中的枚举类似,Java枚举类型中的各个枚举值都有一个数值标识,这个数值可以通过调用枚举值的ordinal方法来获取,如以下代码所示。

这个数值标识是按照枚举值的声明顺序从0开始依次递增的数字。
3.11.3 枚举值的比较
如果只是简单地比较两个枚举类型的值是否相等,那么直接使用“==”即可。如果需要更进一步地比较,则可以使用枚举值的compareTo方法。
我们知道,ordinal方法可以获取枚举值的一个数值标识,可以根据这个数值标识来比较两个枚举值。Java给枚举类型加入了一个compareTo方法,通过比较ordinal的值来比较枚举值,如以下代码所示。

代码的运行结果为:-3
当结果小于0时,compareTo左边的枚举值小于右边的枚举值;当结果为0时,则说明两个枚举值相等;当结果大于0时,则说明compareTo右边的枚举值小于左边的枚举值。
需要读者注意的是,在C++中,可以直接使用“>”“<”等比较运算符来比较两个枚举值的大小,但是在Java中是不允许的。
3.11.4 自定义枚举值
对于枚举来说,既然它在本质上是一个类,那么自然也可以给枚举类型添加方法和字段。我们可以给枚举值定义一些自己的标识数值,如代码3.20所示。

这个枚举类型比较复杂,不但声明了一个常量,还定义了一个构造方法和serialNumber方法。从javap反编译的结果可以得知,Sunday、Monday等这些枚举值只是一个Week类的对象,那么在Week中书写的Sunday、Monday则等价于用new关键字创建对象,于是可以用“Sunday(70)”这种方式来隐式地调用Week的构造方法,并把70赋值给Week类的number字段。我们可以用下面的代码来获取枚举值的number字段值:

为了便于按照number字段进行枚举值的比较,我们可以编写自己的compareNumber方法:

注意,自定义的比较方法不能取名为compareTo,因为所有枚举类型生成的类都是从Enum类继承的,而Enum类中的compareTo方法不能被覆盖,所以不允许自定义的比较方法取名为compareTo。关于继承和覆盖,请读者参看下一章。