update 19 item translation

This commit is contained in:
sjsdfg 2019-09-07 10:11:52 +08:00
parent 0c111d428d
commit 79648b16ae

View File

@ -1,10 +1,10 @@
# 19. 要么设计继承并提供文档说明,要么禁用继承
  条目 18 中提醒你注意继承没有设计和文档说明的「外来」类的子类化的危险。 那么为了继承而设计和文档说明一个类是什么意思呢?
  条目 18 中提醒你注意继承没有设计和文档说明的「外来」类的子类化的危险。 那么对于专门为了继承而设计并且具有良好文档说明的类而言,这又意味着什么呢?
  首先,这个类必须准确地描述重写个方法带来的影响。 换句话说该类必须文档说明可重写方法的自用性self-use。 对于每个公共或受保护的方法,文档必须指明方法调用哪些重写方法,以何种顺序以及每次调用的结果如何影响后续处理。 (重写方法,这里是指非 `final` 修饰的方法,无论是公开还是保护的。)更一般地说,一个类必须文档说明任何可能调用可重写方法的情况。 例如,后台线程或者静态初始化代码块可能会调用这样的方法。
  首先,这个类必须准确地描述重写个方法带来的影响。 换句话说该类必须文档说明可重写方法的自用性self-use。 对于每个 public 或者 protected 的方法,文档必须指明方法调用哪些可重写方法,以何种顺序调用的,以及每次调用的结果又是如何影响后续处理。 (重写方法,这里是指非 `final` 修饰的方法,无论是公开还是保护的。)更一般地说,一个类必须文档说明任何可能调用可重写方法的情况。 例如,后台线程或者静态初始化代码块可能会调用这样的方法。
  调用可重写方法的方法在文档注释结束时包含对这些调用的描述。 这些描述在规范中特定部分,标记为“Implementation Requirements”由 Javadoc 标签 `@implSpec` 生成。 本节介绍该方法的内部工作原理。 下面是从 `java.util.AbstractCollection` 类的规范中拷贝的例子:
  调用可重写方法的方法在文档注释结束时包含对这些调用的描述。 这些描述在规范中特定部分,标记为「Implementation Requirements」由 Javadoc 标签 `@implSpec` 生成。 这段话介绍该方法的内部工作原理。 下面是从 `java.util.AbstractCollection` 类的规范中拷贝的例子:
```java
public boolean remove(Object o)
@ -14,17 +14,17 @@ Removes a single instance of the specified element from this collection, if it i
Implementation Requirements: This implementation iterates over the collection looking for the specified element. If it finds the element, it removes the element from the collection using the iterators remove method. Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collections iterator method does not implement the remove method and this collection contains the specified object.
```
  从该集合中删除指定元素的单个实例如果存在optional 实例操作)。 更正式地说,如果这个集合包含一个或多个这样的元素,删除使得 `Objects.equals(o, e)` 的一个元素 e。 如果此集合包含指定的元素(或者等同于此集合因调用而发生了更改),则返回 true。
  从该集合中删除指定元素的单个实例如果存在optional 实例操作)。 更广义地说,如果这个集合包含一个或多个这样的元素 e就删除其中的一个满足 `Objects.equals(o, e)`元素 e。 如果此集合包含指定的元素(或者等同于此集合因调用而发生了更改),则返回 true。
  **实现要求:** 这个实现迭代遍历集合查找指定元素。 如果找到元素,则使用迭代器的 `remove` 方法从集合中删除元素。 请注意,如果此集合的 `iterator` 方法返回的迭代器未实现 `remove` 方法,并且此集合包含指定的对象,则实现将引发 `UnsupportedOperationException` 异常。
  **实现要求:** 这个实现迭代遍历集合查找指定元素。 如果找到元素,则使用迭代器的 `remove` 方法从集合中删除元素。 请注意,如果此集合的 `iterator` 方法返回的迭代器未实现 `remove` 方法,并且此集合包含指定的对象,则实现将引发 `UnsupportedOperationException` 异常。
  这个文档毫无疑问地说明,重写 `iterator` 方法会影响 `remove` 方法的行为。 它还描述了 `iterator` 方法返回的 `Iterator` 行为将如何影响 `remove` 方法的行为。 与条目 18 中的情况相反,在这种情况下,程序员继承 HashSet 并不能说明重写 `add` 方法是否会影响 `addAll` 方法的行为。
  这个文档清楚地说明,重写 `iterator` 方法将会影响 `remove` 方法的行为。 它还描述了 `iterator` 方法返回的 `Iterator` 行为将如何影响 `remove` 方法的行为。 与条目 18 中的情况相反,在这种情况下,程序员继承 `HashSet` 并不能说明重写 `add` 方法是否会影响 `addAll` 方法的行为。
  但是,这是否违背了一个良好的 API 文档应该描述给定的方法是什么,而不是它是如何做的呢? 是的,它确实!这是继承违反封装这一事实的不幸后果。要文档说明一个类以便可以安全地进行子类化,必须描述清楚那些没有详细说明的实现细节。
  关于程序文档有句格言:好的 API 文档应该描述一个给定的方法做了什么工作,而不是描述它是如何做到的。那么,上面这种做法是否违背了这句格言呢?是的,它确实违背了!这正是继承破坏了封装性所带来的不幸后果。所以,为了设计一个类的文档,以便它能够被安全地子类化,你必须描述清楚那些有可能未定义的实现细节。
  `@implSpec` 标签是在 Java 8 中添加的,并且在 Java 9 中被大量使用。这个标签应该默认启用,但是从 Java 9 开始,除非通过命令行开关`-tag "apiNote:a:API Note:"`,否则 Javadoc 实用工具仍然会忽略它。
  `@implSpec` 标签是在 Java 8 中添加的,并且在 Java 9 中被大量使用。这个标签应该默认启用,但是从 Java 9 开始,除非通过命令行开关`-tag "apiNote:a:API Note:"`,否则 Javadoc 工具仍然会忽略它。
  设计继承涉及的不仅仅是文档说明自用的模式。 为了让程序员能够写出有效的子类而不会带来不适当的痛苦,一个类可能以明智选择的受保护方法的形式提供内部工作,或者在罕见的情况下,提供受保护的属性。 例如,考虑 `java.util.AbstractList` 中的 `removeRange` 方法:
  为了继承而进行的设计不仅仅涉及自用模式的文档设计。为了使程序员能够编写出更加有效的子类,而无须承受不必要的痛苦,**类必须以精心挑选的 protected 方法的形式提供适当的钩子hook以便进入其内部工作中**。或者在罕见的情况下,提供受保护的属性。 例如,考虑 `java.util.AbstractList` 中的 `removeRange` 方法:
```java
protected void removeRange(int fromIndex, int toIndex)
@ -44,11 +44,11 @@ Parameters:
  这个方法是通过列表及其子类的 `clear` 操作来调用的。重写这个方法利用列表内部实现的优势,可以大大提高列表和子类的 `clear` 操作性能。
  实现要求:这个实现获取一个列表迭代器,它位于 `fromIndex` 之前,并重复调用 `ListIterator.remove``ListIterator.next` 方法,直到整个范围被删除。 注意:如果 `ListIterator.remove` 需要线性时间,则此实现需要平方级时间。
  **实现要求:**这个实现获取一个列表迭代器,它位于 `fromIndex` 之前,并重复调用 `ListIterator.remove``ListIterator.next` 方法,直到整个范围被删除。 **注意:如果 `ListIterator.remove` 需要线性时间,则此实现需要平方级时间。**
>参数:
>  fromIndex 要移除的第一个元素的索引
>  toIndex 要移除的最后一个元素之后的索引
参数:
  fromIndex 要移除的第一个元素的索引
  toIndex 要移除的最后一个元素之后的索引
  这个方法对 `List` 实现的最终用户来说是没有意义的。 它仅仅是为了使子类很容易提供一个快速 `clear` 方法。 在没有 `removeRange` 方法的情况下,当在子列表上调用 `clear` 方法,子类将不得不使用平方级的时间,否则,或从头重写整个 `subList` 机制——这不是一件容易的事情!
@ -120,7 +120,7 @@ public final class Sub extends Super {
  你可以机械地消除类的自我使用的重写方法,而不会改变其行为。 将每个可重写的方法的主体移动到一个私有的“帮助器方法”,并让每个可重写的方法调用其私有的帮助器方法。 然后用直接调用可重写方法的专用帮助器方法来替换每个自用的可重写方法。
  总之,设计一个继承类是一件很辛苦的事情。 你必须文档说明所有的自用模式,一旦你文档说明了它们,必须承诺为他们的整个生命周期。 如果你不这样做,子类可能会依赖于父类的实现细节,并且如果父类的实现发生改变,子类可能会损坏。 为了允许其他人编写高效的子类,可能还需要导出一个或多个受保护的方法。 除非你知道有一个真正的子类需要,否则你可能最好是通过声明你的类为 `final` 禁止继承,或者确保没有可访问的构造方法
  简而言之,专门为了继承而设计类是一件很辛苦的工作。你必须建立文档说明其所有的自用模式,并且一旦建立了文档,在这个类的整个生命周期中都必须遵守。如果没有做到,子类就会依赖超类的实现细节,如果超类的实现发生了变化,它就有可能遭到破坏。为了允许其他人能编写出高效的子类,还你必须导出一个或者多个受保护的方法。除非知道真正需要子类,否则最好通过将类声明为 `final`,或者确保没有可访问的构造器来禁止类被继承