Effective Java 第三版——34. 使用枚举类型替代整型常量

Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。

Effective Java, Third Edition

Java支持两种引用类型的特殊用途的系列:一种称为枚举类型的类和一种称为注解类型的接口。 本章讨论使用这些类型系列的最佳实践。

34. 使用枚举类型替代整型常量

枚举是其合法值由一组固定的常量组成的一种类型,例如一年中的季节,太阳系中的行星或一副扑克牌中的套装。 在将枚举类型添加到该语言之前,表示枚举类型的常见模式是声明一组名为int的常量,每个类型的成员都有一个常量:

// The int enum pattern - severely deficient! public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2;

这种被称为int枚举模式的技术有许多缺点。 它没有提供类型安全的方式,也没有提供任何表达力。 如果你将一个Apple传递给一个需要Orange的方法,那么编译器不会出现警告,还会用==运算符比较Apple与Orange,或者更糟糕的是:

// Tasty citrus flavored applesauce! int i = (APPLE_FUJI - ORANGE_TEMPLE) / APPLE_PIPPIN;

请注意,每个Apple常量的名称前缀为APPLE_,每个Orange常量的名称前缀为ORANGE_。 这是因为Java不为int枚举组提供名称空间。 当两个int枚举组具有相同的命名常量时,前缀可以防止名称冲突,例如在ELEMENT_MERCURY和PLANET_MERCURY之间。

使用int枚举的程序很脆弱。 因为int枚举是编译时常量[JLS,4.12.4],所以它们的int值被编译到使用它们的客户端中[JLS,13.1]。 如果与int枚举关联的值发生更改,则必须重新编译其客户端。 如果没有,客户仍然会运行,但他们的行为将是不正确的。

没有简单的方法将int枚举常量转换为可打印的字符串。 如果你打印这样一个常量或者从调试器中显示出来,你看到的只是一个数字,这不是很有用。 没有可靠的方法来迭代组中的所有int枚举常量,甚至无法获得int枚举组的大小。

你可能会遇到这种模式的变体,其中使用了字符串常量来代替int常量。 这种称为字符串枚举模式的变体更不理想。 尽管它为常量提供了可打印的字符串,但它可以导致初级用户将字符串常量硬编码为客户端代码,而不是使用属性名称。 如果这种硬编码的字符串常量包含书写错误,它将在编译时逃脱检测并导致运行时出现错误。 此外,它可能会导致性能问题,因为它依赖于字符串比较。

幸运的是,Java提供了一种避免int和String枚举模式的所有缺点的替代方法,并提供了许多额外的好处。 它是枚举类型[JLS,8.9]。 以下是它最简单的形式:

public enum Apple { FUJI, PIPPIN, GRANNY_SMITH } public enum Orange { NAVEL, TEMPLE, BLOOD }

从表面上看,这些枚举类型可能看起来与其他语言类似,比如C,C ++和C#,但事实并非如此。 Java的枚举类型是完整的类,比其他语言中的其他语言更强大,其枚举本质本上是int值。

Java枚举类型背后的基本思想很简单:它们是通过公共静态final属性为每个枚举常量导出一个实例的类。 由于没有可访问的构造方法,枚举类型实际上是final的。 由于客户既不能创建枚举类型的实例也不能继承它,除了声明的枚举常量外,不能有任何实例。 换句话说,枚举类型是实例控制的(第6页)。 它们是单例(条目 3)的泛型化,基本上是单元素的枚举。

枚举提供了编译时类型的安全性。 如果声明一个参数为Apple类型,则可以保证传递给该参数的任何非空对象引用是三个有效Apple值中的一个。 尝试传递错误类型的值将导致编译时错误,因为会尝试将一个枚举类型的表达式分配给另一个类型的变量,或者使用==运算符来比较不同枚举类型的值。

具有相同名称常量的枚举类型可以和平共存,因为每种类型都有其自己的名称空间。 可以在枚举类型中添加或重新排序常量,而无需重新编译其客户端,因为导出常量的属性在枚举类型与其客户端之间提供了一层隔离:常量值不会编译到客户端,因为它们位于int枚举模式中。 最后,可以通过调用其toString方法将枚举转换为可打印的字符串。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydpss.html