Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。但是Java 9 只是一个过渡版本,所以建议安装JDK 10。
36. 使用EnumSet替代位属性
如果枚举类型的元素主要用于集合中,一般来说使用int枚举模式(条目 34),下面将2的不同倍数赋值给每个常量:
// Bit field enumeration constants - OBSOLETE!
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
// Parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) { ... }
}
这种表示方式允许你使用按位或(or)运算将几个常量合并到一个称为位属性(bit field)的集合中:
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
位属性表示还允许你使用按位算术有效地执行集合运算,如并集和交集。 但是位属性具有int枚举常量等的所有缺点。 当打印为数字时,解释位属性比简单的int枚举常量更难理解。 没有简单的方法遍历所有由位属性表示的元素。 最后,必须预测在编写API时需要的最大位数,并相应地为位属性(通常为int或long)选择一种类型。 一旦你选择了一个类型,你就不能超过它的宽度(32或64位)而不改变API。
一些程序员使用枚举优于int常量,当他们需要传递常量集合时仍然使用位属性。 没有理由这样做,因为存在更好的选择。 java.util包提供了EnumSet
类来有效地表示从单个枚举类型中提取的值集合。 这个类实现了Set接口,提供了所有其他Set实现的丰富性,类型安全性和互操作性。 但是在内部,每个EnumSet都表示为一个位矢量(bit vector)。 如果底层的枚举类型有64个或更少的元素,并且大多数情况下,整个EnumSet
用单个long表示,所以它的性能与位属性的性能相当。 批量操作(如removeAll和retainAll)是使用按位算术实现的,就像你为位属性手动操作一样。 但是完全避免了手动位混乱的丑陋和错误倾向:EnumSet
为你做了很大的努力。
下面是前一个使用枚举和枚举集合替代位属性的示例。 它更短,更清晰,更安全:
// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
// Any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set