effective-java-3rd-chinese/docs/notes/36. 使用EnumSet替代位属性.md
2019-03-14 13:10:17 +08:00

53 lines
3.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`,但是简洁性和性能会受到影响。