枚举类型(enum type) 是指一组固定的常量组成合法值的类型,例如一年中的季节、太阳系中的行星或者一副牌中的花色。
在我们平常的开发中,为表示同种类型的不同种类,经常的做法是声明一组具名的int常量来表示,每个类型成员一个常量,如:
public static final int DAY_MONDAY = 1;
public static final int DAY_TUESDAY = 2;
public static final int DAY_WEDNESDAY = 3;
public static final int DAY_THURSDAY = 4;
public static final int DAY_FRIDAY = 5;
public static final int DAY_SATURDAY = 6;
public static final int DAY_SUNDAY = 7;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
这种方法称做 int枚举模式,这种方式在安全性和使用方便性方面没有任何帮助。
a、将day传到想要orange的方法中,编译器不会警告,执行也不会出现错误;
b、用==操作符将day与orange比较,编译器不会警告,执行也不会出现错误;
c、int枚举是编译时常量,被编译到客户端中,如果枚举常量关联的int发生变化,客户端必须重新编译,如果没有重新编译,程序仍可以运行,但行为就确定了,如DAY_MONDAY关联的常量不再是1,而是0。
d、将int枚举常量翻译成可打印的字符串很麻烦
e、如果想要遍历一个组中的所有int 枚举常量,甚至获得int枚举组的大小,这种实现没有啥方便可靠的方法。
因此,推荐使用枚举类型来代替这种int枚举常量:
public enum DAY {DAY_MONDAY, DAY_TUESDAY, DAY_WEDNESDAY,
DAY_THURSDAY, DAY_FRIDAY, DAY_SATURDAY, DAY_SUNDAY}
public enum ORANGE {ORANGE_NAVEL, ORANGE_TEMPLE, ORANGE_BLOOD}
这种枚举类型,提供了编译时的类型安全检查,如果声明了一个参数的类型为DAY,就可以保证,被传到该参数上的任何非null的对象引用一定属于其他有效值中的一个,试图传递类型错误的值时,会导致编译时错误,就像试图将某种枚举类型的表达式赋给另一种枚举类型的变量,或者试图利用==操作符比较不同枚举类型的值一样。同时包含同名常量的多个枚举类型可以共存,因为每个类型有自己的命名空间,增加或重新排列枚举类型的常量,无需重新编译客户端的代码。如果想获取类型对应的字符串,直接通过toString方法即可。
枚举类型除了完善了int枚举模式的不足之处外,枚举类型还允许添加任意的方法和域,并实现任意的接口。这个有什么用途呢?
a、能够将数据与它的常量关联起来,例如能够返回水果颜色或者水果图片的方法,对于我们的ORANGE类型来说可能就很有好处;
b、你可使用适当的方法来增强枚举类型,枚举类型可以先作为枚举常量的一个简单集合,随着时间的推移在演变成为全功能的抽象。
另外,当你想要每增加一种枚举常量时,需要强制选择一种对应的策略,可以使用枚举提供的策略枚举(strategy enum) 的方式。
枚举是通过共有的静态final域为每个枚举常量导出实例的类,因为没有可以访问的构造器,枚举类型是真正的final,因为客户端既不能创建枚举类型的实例,也不能对他进行扩展,因为很可能没有实例,而只有声明过的枚举常量。换句话说,枚举类型是实例受控的,他们是单例的泛型化,本质上是单元素的枚举。如我们上面的ORANGE枚举,
public enum ORANGE {
ORANGE_NAVEL,
ORANGE_TEMPLE,
ORANGE_BLOOD;
}
在编译完成之后,生成的字节码反编译之后
public final class ORANGE extends Enum
{
public static ORANGE[] values()
{
return (ORANGE[])$VALUES.clone();
}
public static ORANGE valueOf(String name)
{
return (ORANGE)Enum.valueOf(com/example/demo/ORANGE, name);
}
private ORANGE(String s, int i)
{
super(s, i);
}
public static final ORANGE ORANGE_NAVEL;
public static final ORANGE ORANGE_TEMPLE;
public static final ORANGE ORANGE_BLOOD;
private static final ORANGE $VALUES[];
static
{
ORANGE_NAVEL = new ORANGE("ORANGE_NAVEL", 0);
ORANGE_TEMPLE = new ORANGE("ORANGE_TEMPLE", 1);
ORANGE_BLOOD = new ORANGE("ORANGE_BLOOD", 2);
$VALUES = (new ORANGE[] {
ORANGE_NAVEL, ORANGE_TEMPLE, ORANGE_BLOOD
});
}
}
可以看到
a、枚举类是继承于java.lang.Enum的类。
b、枚举值是类对象, 且是静态常量(被static final修饰)。
c、静态代码块内实例化枚举值,由于静态代码块的语法特性,该代码块只执行一次;
d、默认值0、1、2是在编译时生成的。
总而言之,与int常量相比,枚举类型的优势是不言而喻的。枚举的可读性要好很多,也更加安全,功能也更加强大,许多枚举都不需要要显示的构造器或成员,但许多其他枚举则受益于“每个常量与属性的关联”以及“提供行为受这个属性影响的方法”。只有极少数的枚举受益于将多种行为与单个方法关联,在这种相对少见的情况下,特定于常量的方法要优先于启用自由值的枚举,如果多个枚举常量同时共享相同的行为,则考虑策略枚举。