<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## Chapter 4. Classes and Interfaces(類和接口) ### Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用) Item 18 alerted you to the dangers of subclassing a “foreign” class that was not designed and documented for inheritance. So what does it mean for a class to be designed and documented for inheritance? [Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md) 提醒你注意:將不是為繼承設計并且缺少文檔的「外部」類進行子類化的危險。那么,為繼承而設計并且具備文檔的類意味著什么呢? First, the class must document precisely the effects of overriding any method.In other words, **the class must document its self-use of overridable methods.** For each public or protected method, the documentation must indicate which overridable methods the method invokes, in what sequence, and how the results of each invocation affect subsequent processing. (By overridable, we mean nonfinal and either public or protected.) More generally, a class must document any circumstances under which it might invoke an overridable method. For example, invocations might come from background threads or static initializers. 首先,類必須精確地在文檔中記錄覆蓋任何方法的效果。換句話說,類必須在文檔中記錄它對可覆蓋方法的自用性。對于每個公共或受保護的方法,文檔必須指出方法調用的可覆蓋方法、調用順序以及每次調用的結果如何影響后續處理過程。(可覆蓋的意思是非 final 的,公共的或受保護的。)更一般地說,類必須記錄它可能調用可覆蓋方法的所有情況。例如,可能調用來自后臺線程或靜態初始化器的方法。 A method that invokes overridable methods contains a description of these invocations at the end of its documentation comment. The description is in a special section of the specification, labeled “Implementation Requirements,” which is generated by the Javadoc tag @implSpec. This section describes the inner workings of the method. Here’s an example, copied from the specification for java.util.AbstractCollection: 調用可覆蓋方法的方法在其文檔注釋末尾應包含這些調用的描述。描述在規范的一個特殊部分中,標記為「Implementation Requirements(實現需求)」,它由 Javadoc 標簽 `@implSpec` 生成。本節描述該方法的內部工作方式。下面是一個示例,復制自 `java.util.AbstractCollection` 規范: > public boolean remove(Object o) > Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that Objects.equals(o, e), if this collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call). 從此集合中移除指定元素的單個實例,如果存在(可選操作)。更正式地說,如果此集合包含一個或多個這樣的元素,則刪除元素 e,使得 `Objects.equals(o, e)`,如果此 collection 包含指定的元素,則返回 true(或等效地,如果此集合因調用而更改)。 > 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 iterator’s remove method. Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collection’s iterator method does not implement the remove method and this collection contains the specified object. 實現需求:這個實現遍歷集合,尋找指定的元素。如果找到元素,則使用迭代器的 remove 方法從集合中刪除元素。注意,如果這個集合的迭代器方法返回的迭代器沒有實現 remove 方法,并且這個集合包含指定的對象,那么這個實現將拋出 UnsupportedOperationException。 This documentation leaves no doubt that overriding the iterator method will affect the behavior of the remove method. It also describes exactly how the behavior of the Iterator returned by the iterator method will affect the behavior of the remove method. Contrast this to the situation in Item 18, where the programmer subclassing HashSet simply could not say whether overriding the add method would affect the behavior of the addAll method. 這篇文檔無疑說明了重寫迭代器方法將影響 remove 方法的行為。它還準確地描述了迭代器方法返回的迭代器的行為將如何影響 remove 方法的行為。與 [Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md) 中的情況相反,在 [Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md) 中,程序員子類化 HashSet 不能簡單地說覆蓋 add 方法是否會影響 addAll 方法的行為。 But doesn’t this violate the dictum that good API documentation should describe what a given method does and not how it does it? Yes, it does! This is an unfortunate consequence of the fact that inheritance violates encapsulation. To document a class so that it can be safely subclassed, you must describe implementation details that should otherwise be left unspecified. 但是,這是否違背了一個格言:好的 API 文檔應該描述一個給定的方法做什么,而不是如何做?是的,它確實違背了!這是繼承違反封裝這一事實的不幸結果。要為一個類編制文檔,使其能夠安全地子類化,你必須描述實現細節,否則這些細節應該是未指定的。 The @implSpec tag was added in Java 8 and used heavily in Java 9. This tag should be enabled by default, but as of Java 9, the Javadoc utility still ignores it unless you pass the command line switch -tag "apiNote: a :API Note:". `@implSpec` 標記在 Java 8 中添加,在 Java 9 中大量使用。默認情況下應該啟用這個標記,但是在 Java 9 中,Javadoc 實用程序仍然忽略它,除非傳遞命令行開關 `-tag "apiNote: a :API Note:"`。 Designing for inheritance involves more than just documenting patterns of self-use. To allow programmers to write efficient subclasses without undue pain, a class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods or, in rare instances, protected fields. For example, consider the removeRange method from java.util.AbstractList: 為繼承而設計不僅僅是記錄自用性模式。為了允許程序員編寫高效的子類而不受不必要的痛苦,類可能必須以明智地選擇受保護的方法或(在很少的情況下)受保護的字段的形式為其內部工作提供掛鉤。例如,考慮來自 `java.util.AbstractList` 的 removeRange 方法: > protected void removeRange(int fromIndex, int toIndex) > Removes from this list all of the elements whose index is between fromIndex, inclusive, and toIndex, exclusive. Shifts any succeeding elements to the left (reduces their index). This call shortens the list by (toIndex - fromIndex) elements. (If toIndex == fromIndex,this operation has no effect.) 從這個列表中刪除所有索引位于 fromIndex(包含索引)和 toIndex(獨占索引)之間的元素。將任何后續元素移到左邊(減少其索引)。這個調用使用 `(toIndex - fromIndex)` 元素縮短列表。(如果 toIndex == fromIndex,此操作無效。) > This method is called by the clear operation on this list and its sublists.Overriding this method to take advantage of the internals of the list implementation can substantially improve the performance of the clear operation on this list and its sublists. 此方法由此列表及其子列表上的 clear 操作調用。重寫此方法以利用列表實現的內部特性,可以顯著提高對該列表及其子列表的 clear 操作的性能。 > Implementation Requirements: This implementation gets a list iterator positioned before fromIndex and repeatedly calls ListIterator.next followed by ListIterator.remove, until the entire range has been removed. Note: If ListIterator.remove requires linear time, this implementation requires quadratic time. 實現需求:該實現獲取位于 fromIndex 之前的列表迭代器,并依次重復調用 `ListIterator.next` 和 `ListIterator.remove`,直到刪除整個范圍的內容。注意:如果 `ListIterator.remove` 需要線性時間,這個實現需要平方級的時間。 > Parameters: 參數 > fromIndex index of first element to be removed. 要刪除的第一個元素的 fromIndex 索引。 > toIndex index after last element to be removed. 要刪除的最后一個元素后的索引。 This method is of no interest to end users of a List implementation. It is provided solely to make it easy for subclasses to provide a fast clear method on sublists. In the absence of the removeRange method, subclasses would have to make do with quadratic performance when the clear method was invoked on sublists or rewrite the entire subList mechanism from scratch— not an easy task! 此方法對列表實現的最終用戶沒有任何興趣。它的提供只是為了讓子類更容易在子列表上提供快速清晰的方法。在沒有 removeRange 方法的情況下,當在子列表上調用 clear 方法或從頭重寫整個子列表機制時,子類將不得不處理二次性能——這不是一項簡單的任務! So how do you decide what protected members to expose when you design a class for inheritance? Unfortunately, there is no magic bullet. The best you can do is to think hard, take your best guess, and then test it by writing subclasses.You should expose as few protected members as possible because each one represents a commitment to an implementation detail. On the other hand, you must not expose too few because a missing protected member can render a class practically unusable for inheritance. 那么,在為繼承設計類時,如何決定要公開哪些受保護的成員呢?不幸的是,沒有靈丹妙藥。你能做的最好的事情就是認真思考,做出最好的猜測,然后通過編寫子類來測試它。你應該盡可能少地公開受保護的成員,因為每個成員都表示對實現細節的承諾。另一方面,你不能公開太多,因為缺少受保護的成員會導致類實際上無法用于繼承。 **The only way to test a class designed for inheritance is to write subclasses.** If you omit a crucial protected member, trying to write a subclass will make the omission painfully obvious. Conversely, if several subclasses are written and none uses a protected member, you should probably make it private. Experience shows that three subclasses are usually sufficient to test an extendable class. One or more of these subclasses should be written by someone other than the superclass author. **測試為繼承而設計的類的唯一方法是編寫子類。** 如果你忽略了一個關鍵的受保護成員,那么嘗試編寫子類將使遺漏變得非常明顯。相反,如果編寫了幾個子類,而沒有一個子類使用受保護的成員,則應該將其設置為私有。經驗表明,三個子類通常足以測試一個可擴展類。這些子類中的一個或多個應該由超類作者以外的其他人編寫。 When you design for inheritance a class that is likely to achieve wide use, realize that you are committing forever to the self-use patterns that you document and to the implementation decisions implicit in its protected methods and fields. These commitments can make it difficult or impossible to improve the performance or functionality of the class in a subsequent release. Therefore,**you must test your class by writing subclasses before you release it.** 當你為繼承設計一個可能獲得廣泛使用的類時,請意識到你將永遠致力于你所記錄的自使用模式,以及在其受保護的方法和字段中隱含的實現決策。這些承諾會使在后續版本中改進類的性能或功能變得困難或不可能。因此,**你必須在發布類之前通過編寫子類來測試類。** Also, note that the special documentation required for inheritance clutters up normal documentation, which is designed for programmers who create instances of your class and invoke methods on them. As of this writing, there is little in the way of tools to separate ordinary API documentation from information of interest only to programmers implementing subclasses. 另外,請注意,繼承所需的特殊文檔會使普通文檔變得混亂,這種文檔是為那些創建類實例并在其上調用方法的程序員設計的。在撰寫本文時,很少有工具能夠將普通 API 文檔與只對實現子類的程序員感興趣的信息分離開來。 There are a few more restrictions that a class must obey to allow inheritance.**Constructors must not invoke overridable methods,** directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected. To make this concrete, here’s a class that violates this rule: 為了允許繼承,類必須遵守更多的限制。**構造函數不能直接或間接調用可重寫的方法。** 如果你違反了這個規則,程序就會失敗。超類構造函數在子類構造函數之前運行,因此在子類構造函數運行之前將調用子類中的覆蓋方法。如果重寫方法依賴于子類構造函數執行的任何初始化,則該方法的行為將不像預期的那樣。為了使其具體化,下面是一個違反此規則的類: ``` public class Super { // Broken - constructor invokes an overridable method public Super() { overrideMe(); } public void overrideMe() { } } ``` Here’s a subclass that overrides the overrideMe method, which is erroneously invoked by Super’s sole constructor: 下面是覆蓋 overrideMe 方法的子類,Super 的唯一構造函數錯誤地調用了 overrideMe 方法: ``` public final class Sub extends Super { // Blank final, set by constructor private final Instant instant; Sub() { instant = Instant.now(); } // Overriding method invoked by superclass constructor @Override public void overrideMe() { System.out.println(instant); } public static void main(String[] args) { Sub sub = new Sub(); sub.overrideMe(); } } ``` You might expect this program to print out the instant twice, but it prints out null the first time because overrideMe is invoked by the Super constructor before the Sub constructor has a chance to initialize the instant field. Note that this program observes a final field in two different states! Note also that if overrideMe had invoked any method on instant, it would have thrown a NullPointerException when the Super constructor invoked overrideMe. The only reason this program doesn’t throw a NullPointerException as it stands is that the println method tolerates null parameters. 你可能希望這個程序打印兩次 instant,但是它第一次打印 null,因為在子構造函數有機會初始化 instant 字段之前,超級構造函數調用了 overrideMe。注意,這個程序觀察了兩個不同狀態的最后一個字段!還要注意,如果 overrideMe 立即調用了任何方法,那么當超級構造函數調用 overrideMe 時,它會拋出一個 NullPointerException。這個程序不拋出 NullPointerException 的唯一原因是 println 方法允許空參數。 Note that it is safe to invoke private methods, final methods, and static methods, none of which are overridable, from a constructor. 注意,從構造函數調用私有方法、最終方法和靜態方法是安全的,它們都是不可覆蓋的。 The Cloneable and Serializable interfaces present special difficulties when designing for inheritance. It is generally not a good idea for a class designed for inheritance to implement either of these interfaces because they place a substantial burden on programmers who extend the class. There are,however, special actions that you can take to allow subclasses to implement these interfaces without mandating that they do so. These actions are described in Item 13 and Item 86. 可克隆和可序列化的接口在設計繼承時存在特殊的困難。對于為繼承而設計的類來說,實現這兩種接口都不是一個好主意,因為它們給擴展類的程序員帶來了沉重的負擔。但是,你可以采取一些特殊的操作來允許子類實現這些接口,而無需強制它們這樣做。[Item-13](/Chapter-3/Chapter-3-Item-13-Override-clone-judiciously.md) 和 [Item-86](/Chapter-12/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md) 敘述了這些行動。 If you do decide to implement either Cloneable or Serializable in a class that is designed for inheritance, you should be aware that because the clone and readObject methods behave a lot like constructors, a similar restriction applies: neither clone nor readObject may invoke an overridable method, directly or indirectly. In the case of readObject, the overriding method will run before the subclass’s state has been deserialized. In the case of clone, the overriding method will run before the subclass’s clone method has a chance to fix the clone’s state. In either case, a program failure is likely to follow. In the case of clone, the failure can damage the original object as well as the clone. This can happen, for example, if the overriding method assumes it is modifying the clone’s copy of the object’s deep structure, but the copy hasn’t been made yet. 如果你確實決定在為繼承而設計的類中實現 Cloneable 或 Serializable,那么你應該知道,由于 clone 和 readObject 方法的行為與構造函數非常相似,因此存在類似的限制:clone 和 readObject 都不能直接或間接調用可覆蓋的方法。對于 readObject,覆蓋方法將在子類的狀態反序列化之前運行。在 clone 的情況下,覆蓋方法將在子類的 clone 方法有機會修復 clone 的狀態之前運行。在任何一種情況下,程序失敗都可能隨之而來。在 clone 的情況下,失敗可以破壞原始對象和 clone。例如,如果覆蓋方法假設它正在修改對象的深層結構的 clone 副本,但是復制還沒有完成,那么就會發生這種情況。 Finally, if you decide to implement Serializable in a class designed for inheritance and the class has a readResolve or writeReplace method,you must make the readResolve or writeReplace method protected rather than private. If these methods are private, they will be silently ignored by subclasses. This is one more case where an implementation detail becomes part of a class’s API to permit inheritance. 最后,如果你決定在一個為繼承而設計的類中實現 Serializable,并且這個類有一個 readResolve 或 writeReplace 方法,那么你必須使 readResolve 或 writeReplace 方法為 protected,而不是 private。如果這些方法是 private 的,它們將被子類靜靜地忽略。這是實現細節成為類 API 允許繼承的一部分的又一種情況。 By now it should be apparent that designing a class for inheritance requires great effort and places substantial limitations on the class. This is not a decision to be undertaken lightly. There are some situations where it is clearly the right thing to do, such as abstract classes, including skeletal implementations of interfaces (Item 20). There are other situations where it is clearly the wrong thing to do, such as immutable classes (Item 17). 到目前為止,顯然為繼承而設計一個類需要付出很大的努力,并且對類有很大的限制。這不是一個可以輕易作出的決定。在某些情況下,這樣做顯然是正確的,例如抽象類,包括接口的骨架實現([Item-20](/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md))。還有一些情況顯然是錯誤的,比如不可變類([Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。 But what about ordinary concrete classes? Traditionally, they are neither final nor designed and documented for subclassing, but this state of affairs is dangerous. Each time a change is made in such a class, there is a chance that subclasses extending the class will break. This is not just a theoretical problem.It is not uncommon to receive subclassing-related bug reports after modifying the internals of a nonfinal concrete class that was not designed and documented for inheritance. 但是普通的具體類呢?傳統上,它們既不是最終的,也不是為子類化而設計和記錄的,但這種狀態是危險的。每當在這樣的類中進行更改時,擴展類的子類就有可能中斷。這不僅僅是一個理論問題。在修改未為繼承而設計和記錄文檔的非最終具體類的內部結構后,接收與子類相關的 bug 報告并不罕見。 The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed. There are two ways to prohibit subclassing. The easier of the two is to declare the class final. The alternative is to make all the constructors private or package-private and to add public static factories in place of the constructors. This alternative, which provides the flexibility to use subclasses internally, is discussed in Item 17. Either approach is acceptable. 這個問題的最佳解決方案是禁止在沒有設計和文檔記錄的類中進行子類化。有兩種方法可以禁止子類化。兩者中比較容易的是聲明類 final。另一種方法是將所有構造函數變為私有或包私有,并在構造函數的位置添加公共靜態工廠。這個替代方案提供了內部使用子類的靈活性,在 [Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md) 中進行了討論。兩種方法都可以接受。 This advice may be somewhat controversial because many programmers have grown accustomed to subclassing ordinary concrete classes to add facilities such as instrumentation, notification, and synchronization or to limit functionality. If a class implements some interface that captures its essence, such as Set, List, or Map, then you should feel no compunction about prohibiting subclassing. The wrapper class pattern, described in Item 18, provides a superior alternative to inheritance for augmenting the functionality. 這個建議可能有點爭議,因為許多程序員已經習慣了子類化普通的具體類,以添加工具、通知和同步等功能或限制功能。如果一個類實現了某個接口,該接口捕獲了它的本質,例如 Set、List 或 Map,那么你不應該對禁止子類化感到內疚。在 [Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md) 中描述的包裝器類模式提供了一種優于繼承的方法來增強功能。 If a concrete class does not implement a standard interface, then you may inconvenience some programmers by prohibiting inheritance. If you feel that you must allow inheritance from such a class, one reasonable approach is to ensure that the class never invokes any of its overridable methods and to document this fact. In other words, eliminate the class’s self-use of overridable methods entirely. In doing so, you’ll create a class that is reasonably safe to subclass. Overriding a method will never affect the behavior of any other method. 如果一個具體的類沒有實現一個標準的接口,那么你可能會因為禁止繼承而給一些程序員帶來不便。如果你認為必須允許繼承此類類,那么一種合理的方法是確保該類永遠不會調用其任何可重寫的方法,并記錄這一事實。換句話說,消除類的自用 overridable You can eliminate a class’s self-use of overridable methods mechanically, without changing its behavior. Move the body of each overridable method to a private “helper method” and have each overridable method invoke its private helper method. Then replace each self-use of an overridable method with a direct invocation of the overridable method’s private helper method. 你可以在不改變類行為的情況下,機械地消除類對可重寫方法的自使用。將每個可覆蓋方法的主體移動到一個私有的「助手方法」,并讓每個可覆蓋方法調用它的私有助手方法。然后,用可覆蓋方法的私有助手方法的直接調用替換可覆蓋方法的每個自使用。 In summary, designing a class for inheritance is hard work. You must document all of its self-use patterns, and once you’ve documented them, you must commit to them for the life of the class. If you fail to do this, subclasses may become dependent on implementation details of the superclass and may break if the implementation of the superclass changes. To allow others to write efficient subclasses, you may also have to export one or more protected methods.Unless you know there is a real need for subclasses, you are probably better off prohibiting inheritance by declaring your class final or ensuring that there are no accessible constructors. 總之,為繼承設計一個類是一項艱苦的工作。你必須記錄所有的自用模式,并且一旦你記錄了它們,你就必須在整個類的生命周期中都遵守它們。如果沒有這樣做,子類可能會依賴于超類的實現細節,如果超類的實現發生變化,子類可能會崩潰。為了允許其他人編寫高效的子類,你可能還需要導出一個或多個受保護的方法。除非你知道確實需要子類,否則最好通過聲明類為 final 或確保沒有可訪問的構造函數的方式來禁止繼承。 --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-4/Chapter-4-Introduction.md)** - **Previous Item(上一條目):[Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)** - **Next Item(下一條目):[Item 20: Prefer interfaces to abstract classes(接口優于抽象類)](/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看