mirror of
https://github.com/sjsdfg/effective-java-3rd-chinese.git
synced 2025-01-01 07:50:33 +08:00
36. 使用 EnumSet 替代位属性
This commit is contained in:
parent
0dac4255e0
commit
2d7d0f54b3
52
36. 使用EnumSet替代位属性.md
Normal file
52
36. 使用EnumSet替代位属性.md
Normal file
@ -0,0 +1,52 @@
|
||||
# 36. 使用 EnumSet 替代位属性
|
||||
|
||||
如果枚举类型的元素主要用于集合中,一般来说使用 int 枚举模式(条目 34),下面将 2 的不同倍数赋值给每个常量:
|
||||
|
||||
```Java
|
||||
// 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)的集合中:
|
||||
|
||||
```Java
|
||||
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` 为你做了很大的努力。
|
||||
|
||||
下面是前一个使用枚举和枚举集合替代位属性的示例。 它更短,更清晰,更安全:
|
||||
|
||||
```Java
|
||||
// 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<Style> styles) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
这里是将 `EnumSet` 实例传递给 `applyStyles` 方法的客户端代码。 `EnumSet` 类提供了一组丰富的静态工厂,可以轻松创建集合,其中一个代码如下所示:
|
||||
|
||||
```Java
|
||||
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
|
||||
```
|
||||
|
||||
请注意,`applyStyles` 方法采用`Set<Style>`而不是`EnumSet<Style>`参数。 尽管所有客户端都可能会将 `EnumSet` 传递给该方法,但接受接口类型而不是实现类型通常是很好的做法(条目 64)。 这允许一个不寻常的客户端通过其他 Set 实现的可能性。
|
||||
|
||||
总之,仅仅因为枚举类型将被用于集合中,所以没有理由用位属性来表示它。 `EnumSet` 类将位属性的简洁性和性能与条目 34 中所述的枚举类型的所有优点相结合。`EnumSet` 的一个真正缺点是,它不像 Java 9 那样创建一个不可变的 `EnumSet`,但是在即将发布的版本中可能会得到补救。 同时,你可以用 `Collections.unmodifiableSet` 封装一个 `EnumSet`,但是简洁性和性能会受到影响。
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user