mirror of
https://github.com/sjsdfg/effective-java-3rd-chinese.git
synced 2024-12-26 12:50:29 +08:00
修改第34、35、36条的翻译内容 (#46)
* 修改第34条 * 增加第35条对单词intervening的翻译 * 修改条目36的翻译内容 * fix typo * 增加单词的空格
This commit is contained in:
parent
0afc7e0a11
commit
06362dd1d5
@ -3,7 +3,7 @@
|
||||
|
||||
# 34. 使用枚举类型替代整型常量
|
||||
|
||||
枚举是其合法值由一组固定的常量组成的一种类型,例如一年中的季节,太阳系中的行星或一副扑克牌中的套装。 在将枚举类型添加到该语言之前,表示枚举类型的常见模式是声明一组名为 `int` 的常量,每个类型的成员都有一个常量:
|
||||
枚举是其合法值由一组固定的常量组成的一种类型,例如一年中的季节,太阳系中的行星或一副扑克牌中的花色。 在将枚举类型添加到该语言之前,表示枚举类型的常见模式是声明一组名为 `int` 的常量,每个类型的成员都有一个常量:
|
||||
|
||||
```java
|
||||
// The int enum pattern - severely deficient!
|
||||
@ -113,9 +113,9 @@ Weight on URANUS is 167.398264
|
||||
Weight on NEPTUNE is 210.208751
|
||||
```
|
||||
|
||||
直到 2006 年,在 Java 中加入枚举两年之后,冥王星不再是一颗行星。 这引发了一个问题:「当你从枚举类型中移除一个元素时会发生什么?」答案是,任何不引用移除元素的客户端程序都将继续正常工作。 所以,举例来说,我们的 `WeightTable` 程序只需要打印一行少一行的表格。 什么是客户端程序引用删除的元素(在这种情况下,`Planet.Pluto`)? 如果重新编译客户端程序,编译将会失败并在引用前一个星球的行处提供有用的错误消息; 如果无法重新编译客户端,它将在运行时从此行中引发有用的异常。 这是你所希望的最好的行为,远远好于你用 `int` 枚举模式得到的结果。
|
||||
直到 2006 年,在 Java 中加入枚举两年之后,冥王星不再是一颗行星。 这引发了一个问题:「当你从枚举类型中移除一个元素时会发生什么?」答案是,任何不引用移除元素的客户端程序都将继续正常工作。 所以,举例来说,我们的 `WeightTable` 程序将会打印一个少一行的表格。 那么客户端程序引用删除的元素(在本例中是`Planet.Pluto`)会如何? 如果重新编译客户端程序,编译将会失败并在引用删除的星球的行处提供有用的错误消息; 如果无法重新编译客户端,它将在运行时从此行中引发有用的异常。 这是你所希望的最好的行为,远远好于你用 `int` 枚举模式得到的结果。
|
||||
|
||||
一些与枚举常量相关的行为只需要在定义枚举的类或包中使用。 这些行为最好以私有或包级私有方式实现。 然后每个常量携带一个隐藏的行为集合,允许包含枚举的类或包在与常量一起呈现时作出适当的反应。 与其他类一样,除非你有一个令人信服的理由将枚举方法暴露给它的客户端,否则将其声明为私有的,如果需要的话将其声明为包级私有(详见第 15 条)。
|
||||
一些与枚举常量相关的行为只需要在定义枚举的类或包中使用。 这些行为最好以私有或包级私有方式实现。 然后每个常量携带一个隐藏的行为集合,这些行为允许包含枚举的类或包在呈现常量时作出适当的反应。 与其他类一样,除非你有一个令人信服的理由将枚举方法暴露给它的客户端,否则将其声明为私有的,如果需要的话将其声明为包级私有(详见第 15 条)。
|
||||
|
||||
如果一个枚举是广泛使用的,它应该是一个顶级类; 如果它的使用与特定的顶级类绑定,它应该是该顶级类的成员类(详见第 24 条)。 例如,`java.math.RoundingMode` 枚举表示小数部分的舍入模式。 `BigDecimal` 类使用了这些舍入模式,但它们提供了一种有用的抽象,它并不与 `BigDecimal` 有根本的联系。 通过将 `RoundingMode` 设置为顶层枚举,类库设计人员鼓励任何需要舍入模式的程序员重用此枚举,从而提高跨 `API` 的一致性。
|
||||
|
||||
|
@ -14,7 +14,7 @@ public enum Ensemble {
|
||||
|
||||
虽然这个枚举能正常工作,但对于维护来说则是一场噩梦。如果常量被重新排序,`numberOfMusicians` 方法将会中断。 如果你想添加一个与你已经使用的 `int` 值相关的第二个枚举常量,则没有那么好运了。 例如,为双四重奏(double quartet)添加一个常量可能会很好,它就像八重奏一样,由 8 位演奏家组成,但是没有办法做到这一点。
|
||||
|
||||
此外,如果没有给所有这些 `int` 值添加常量,也不能为某个 `int` 值添加一个常量。例如,假设你想要添加一个常量,表示一个由 12 位演奏家组成的三重四重奏(triple quartet)。对于由 11 个演奏家组成的合奏曲,并没有标准的术语,因此你不得不为未使用的 `int` 值(11)添加一个虚拟常量(dummy constant)。最多看起来就是有些不好看。如果许多 `int` 值是未使用的,则是不切实际的。
|
||||
此外,如果没有给所有中间的 `int` 值添加常量,就不能为这个 `int` 值添加一个常量。例如,假设你想要添加一个常量,表示一个由 12 位演奏家组成的三重四重奏(triple quartet)。对于由 11 个演奏家组成的合奏曲,并没有标准的术语,因此你不得不为未使用的 `int` 值(11)添加一个虚拟常量(dummy constant)。最多看起来就是有些不好看。如果许多 `int` 值是未使用的,则是不切实际的。
|
||||
|
||||
幸运的是,这些问题有一个简单的解决方案。 **永远不要从枚举的序号中得出与它相关的值; 请将其保存在实例属性中:**
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 36. 使用 EnumSet 替代位属性
|
||||
|
||||
如果枚举类型的元素主要用于集合中,一般来说使用 int 枚举模式(详见第 34 条),下面将 2 的不同倍数赋值给每个常量:
|
||||
如果枚举类型的元素主要用于集合中,则传统上使用 int 枚举模式(详见第 34 条),将 2 的不同次幂赋值给每个常量:
|
||||
|
||||
```java
|
||||
// Bit field enumeration constants - OBSOLETE!
|
||||
@ -15,17 +15,17 @@ public class Text {
|
||||
}
|
||||
```
|
||||
|
||||
这种表示方式允许你使用按位或(or)运算将几个常量合并到一个称为位属性(bit field)的集合中:
|
||||
这种表示方式允许你使用按位或(or)运算将几个常量合并到一个称为位域(bit field)的集合中:
|
||||
|
||||
```java
|
||||
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
|
||||
```
|
||||
|
||||
位属性表示还允许你使用按位算术有效地执行集合运算,如并集和交集。 但是位属性具有 `int` 枚举常量等的所有缺点。 当打印为数字时,解释位属性比简单的 `int` 枚举常量更难理解。 没有简单的方法遍历所有由位属性表示的元素。 最后,必须预测在编写 API 时需要的最大位数,并相应地为位属性(通常为 `int` 或 `long`)选择一种类型。 一旦你选择了一个类型,你就不能超过它的宽度(32 或 64 位)而不改变 API。
|
||||
位域表示还允许你使用按位算术有效地执行集合运算,如并集和交集。 但是位域具有 `int` 枚举常量等的所有缺点。 当打印为数字时,解释位域比简单的 `int` 枚举常量更难理解。 没有简单的方法遍历所有由位域表示的元素。 最后,必须预测在编写 API 时需要的最大位数,并相应地为位域(通常为 `int` 或 `long`)选择一种类型。 一旦你选择了一个类型,你就不能在不改变 API 的情况下超过它的宽度(32 或 64 位)。
|
||||
|
||||
一些程序员使用枚举优于 `int` 常量,当他们需要传递常量集合时仍然使用位属性。 没有理由这样做,因为存在更好的选择。 `java.util` 包提供了 `EnumSet` 类来有效地表示从单个枚举类型中提取的值集合。 这个类实现了 `Set` 接口,提供了所有其他 `Set` 实现的丰富性,类型安全性和互操作性。 但是在内部,每个 `EnumSet` 都表示为一个位矢量(bit vector)。 如果底层的枚举类型有 64 个或更少的元素,并且大多数情况下,整个 `EnumSet` 用单个 `long` 表示,所以它的性能与位属性的性能相当。 批量操作(如 `removeAll` 和 `retainAll`)是使用按位算术实现的,就像你为位属性手动操作一样。 但是完全避免了手动位混乱的丑陋和错误倾向:`EnumSet` 为你做了很大的努力。
|
||||
当需要传递一组常量时,一些优先使用枚举而不是 `int` 常量的程序员仍然坚持使用位域。 没有理由这样做,因为存在更好的选择。 `java.util` 包提供了 `EnumSet` 类来有效地表示从单个枚举类型中提取的值集合。 这个类实现了 `Set` 接口,提供了所有其他 `Set` 实现的丰富性,类型安全性和互操作性。 但是在内部,每个 `EnumSet` 都表示为一个位向量(bit vector)。 如果底层的枚举类型有 64 个或更少的元素,并且大多数情况下,整个 `EnumSet` 用单个 `long` 表示,所以它的性能与位域的性能相当。 批量操作(如 `removeAll` 和 `retainAll`)是使用按位算术实现的,就像你手动操作位域一样。 但是完全避免了手动进行位操作导致的丑陋和潜在错误:`EnumSet` 为你完成这一困难工作。
|
||||
|
||||
下面是前一个使用枚举和枚举集合替代位属性的示例。 它更短,更清晰,更安全:
|
||||
下面是前一个使用枚举和枚举集合替代位域的示例。 它更短,更清晰,更安全:
|
||||
|
||||
```java
|
||||
// EnumSet - a modern replacement for bit fields
|
||||
@ -45,7 +45,7 @@ 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`,但是简洁性和性能会受到影响。
|
||||
总之,**仅仅因为枚举类型将被用于集合中,就没有理由用位域来表示它。** `EnumSet` 类结合了位域的简洁性和性能以及条目 34 中所述的枚举类型的所有优点。截至 Java 9 ,`EnumSet` 的一个真正缺点是不能创建一个不可变的 `EnumSet`,但是在之后的发行版本中这可能会得到纠正。 同时,你可以用 `Collections.unmodifiableSet` 封装一个 `EnumSet`,但是简洁性和性能会受到影响。
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user