第6章 枚举和注解
第30条:用 enum 代替 int 常量
在没有 enum 之前表示枚举类型的常用模式时声明一组具名的 int 常量或 String 常量,这种方式有非常多不足,在类型安全性和使用方便性方面没有帮助。
Java 1.5 版本提供了 enum 枚举,其基本想法是:通过公有的静态 final 域为每个枚举常量导出实例的类,不提供可访问的构造器,不能扩展,所以枚举类型是实例受控的。
枚举提供了编译时的类型安全,如果某个参数指定就是枚举类型,那么传递到这里就一定是枚举值之中的一个。
枚举类型还允许添加任意的域和方法(这里可以将它看成就是一个普通的类),数据,行为和常量关联起来。
在枚举中有一种方法可以将不同的行为与每个枚举常量关联起来,而不是去根据类型判断执行不同的行为:在枚举类型中声明一个抽象的 apply 方法,并在特定于常量的类主题中覆盖它。这种方法被称为特定于常量的方法实现。
代码如下:
public enum Operation{
PLUS (double apply(double x, double y){return x + y;}),
MINUS {double apply(double x, double y){return x - y;}},
TIMES {double apply(double x, double y){return x * y;}},
DIVIDE {double apply(double x, double y){return x / y;}};
abstract double apply(double x, double y);
}
与 int 常量相比,枚举有一个小小的性能缺点,即装载和初始化枚举时会有空间和时间的成本。所以在早期的 Android 版本中并不推荐使用枚举,过于占用内存,后来有所改善,可以适当地使用。
第31条:用实例域代替序数
许多枚举天生就合一个单独的 int 值相关联,而所有枚举都有一个 ordinal 方法,它返回每一个枚举常量在类型中的数字位置,这个就是序数。
不建议使用序数的原因是:不好维护,一旦重新排序了,之前依靠 oridinal 数字的功能就会被破坏;也无法单独给某个 int 值添加常量。
所以,推荐的做法是永远不要根据枚举的序数导出与它相关联的值,而是要将它保存在一个实例域中:
public enum Ensemble {
SOLO(1), DUET(2),TRIO(3),
QUARTET(4),QUINTET(5);
private final int numberOfMusicians;
Ensemble(int size) {this.numberOfMusicians = size;}
public int numberOfMusicians(){return numberOfMusicians;}
}
第32条:用 EnumSet 代替位域
如果一个枚举类型的元素主要用在集合中,一般就使用 int 枚举模式,将2的不同倍数赋予每个常量
public class Text{
public static final int STYLE_BOLD = 1; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUCH = 1 << 3; // 8
}
这个就是位域,可以用 OR 位运算将几个常量合并到一个集合中,也可以使用位操作高效地执行像联合和交集这样的集合操作。但是位域有着 int 枚举常量的所有缺点,甚至更多,打印的时候难以阅读,遍历位域表示的所有元素也不容易。
EnumSet 类有效地表示从单个枚举类型中提取的多个值的多个集合。在其内部实现上,每个 EnumSet 内容都表示为位矢量,如果底层的枚举类型有64或更少的元素,整个 EnumSet 用单个 long 表示,如果多于64,则用 long[] 表示。因此它的性能比得上位域的性能。
public class Text{
public enum Style {BOLD, ITALIC, UNDERLINE, STRIKETHROUCH}
// any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set