## Chapter 4. Classes and Interfaces(類和接口)
### Item 21: Design interfaces for posterity(為后代設計接口)
Prior to Java 8, it was impossible to add methods to interfaces without breaking existing implementations. If you added a new method to an interface, existing implementations would, in general, lack the method, resulting in a compile-time error. In Java 8, the default method construct was added [JLS 9.4], with the intent of allowing the addition of methods to existing interfaces. But adding new methods to existing interfaces is fraught with risk.
在 Java 8 之前,在不破壞現有實現的情況下向接口添加方法是不可能的。如果在接口中添加新方法,通常導致現有的實現出現編譯時錯誤,提示缺少該方法。在 Java 8 中,添加了默認的方法構造 [JLS 9.4],目的是允許向現有接口添加方法。但是向現有接口添加新方法充滿了風險。
The declaration for a default method includes a default implementation that is used by all classes that implement the interface but do not implement the default method. While the addition of default methods to Java makes it possible to add methods to an existing interface, there is no guarantee that these methods will work in all preexisting implementations. Default methods are “injected” into existing implementations without the knowledge or consent of their implementors. Before Java 8, these implementations were written with the tacit understanding that their interfaces would never acquire any new methods.
默認方法的聲明包括一個默認實現,所有實現接口但不實現默認方法的類都使用這個默認實現。雖然 Java 使得向現有接口添加方法成為可能,但不能保證這些方法在所有現有實現中都能工作。默認方法被「注入」到現有的實現中,而無需實現者的知情或同意。在 Java 8 之前,編寫這些實現時都默認它們的接口永遠不會獲得任何新方法。
Many new default methods were added to the core collection interfaces in Java 8, primarily to facilitate the use of lambdas (Chapter 6). The Java libraries’ default methods are high-quality general-purpose implementations, and in most cases, they work fine. **But it is not always possible to write a default method that maintains all invariants of every conceivable implementation.**
Java 8 的核心集合接口增加了許多新的默認方法,主要是為了方便 lambda 表達式的使用(Chapter 6)。**但是,并不總是能夠編寫一個默認方法來維護每個實現所有不變性**
For example, consider the removeIf method, which was added to the Collection interface in Java 8. This method removes all elements for which a given boolean function (or predicate) returns true. The default implementation is specified to traverse the collection using its iterator, invoking the predicate on each element, and using the iterator’s remove method to remove the elements for which the predicate returns true. Presumably the declaration looks something like this:
例如,考慮在 Java 8 中被添加到集合接口中的 removeIf 方法。該方法刪除了給定的布爾函數(或 predicate)返回 true 的所有元素。指定默認實現,以使用迭代器遍歷集合,在每個元素上調用 predicate,并使用迭代器的 remove 方法刪除 predicate 返回 true 的元素。聲明大概是這樣的:
```
// Default method added to the Collection interface in Java 8
default boolean removeif(predicate<? super e> filter) {
objects.requirenonnull(filter);
boolean result = false;
for (iterator<e> it = iterator(); it.hasnext(); ) {
if (filter.test(it.next())) {
it.remove();
result = true;
}
}
return result;
}
```
This is the best general-purpose implementation one could possibly write for the removeIf method, but sadly, it fails on some real-world Collection implementations. For example, consider org.apache.commons.collections4.collection.SynchronizedCollection. This class, from the Apache Commons library, is similar to the one returned by the static factory Collections.synchronizedCollection in java.util. The Apache version additionally provides the ability to use a client-supplied object for locking, in place of the collection. In other words, it is a wrapper class (Item 18), all of whose methods synchronize on a locking object before delegating to the wrapped collection.
這是為 removeIf 方法編寫的最好的通用實現,但遺憾的是,它在實際使用的一些 Collection 實現中導致了問題。例如,考慮 `org.apache.commons.collections4.collection.SynchronizedCollection`。這個類來自 Apache Commons 庫,類似于 `java.util` 提供的靜態工廠`Collections.synchronizedCollection`。Apache 版本還提供了使用客戶端提供的對象進行鎖定的功能,以代替集合。換句話說,它是一個包裝器類([Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)),其所有方法在委托給包裝集合之前同步鎖定對象。
The Apache SynchronizedCollection class is still being actively maintained, but as of this writing, it does not override the removeIf method. If this class is used in conjunction with Java 8, it will therefore inherit the default implementation of removeIf, which does not, indeed cannot, maintain the class’s fundamental promise: to automatically synchronize around each method invocation. The default implementation knows nothing about synchronization and has no access to the field that contains the locking object. If a client calls the removeIf method on a SynchronizedCollection instance in the presence of concurrent modification of the collection by another thread, a ConcurrentModificationException or other unspecified behavior may result.
Apache SynchronizedCollection 類仍然得到了積極的維護,但是在編寫本文時,它沒有覆蓋 removeIf 方法。如果這個類與 Java 8 一起使用,那么它將繼承 removeIf 的默認實現,而 removeIf 并不能維護類的基本承諾:自動同步每個方法調用。默認實現對同步一無所知,也無法訪問包含鎖定對象的字段。如果客戶端在 SynchronizedCollection 實例上調用 removeIf 方法,而另一個線程同時修改了集合,那么可能會導致 ConcurrentModificationException 或其他未指定的行為。
In order to prevent this from happening in similar Java platform libraries implementations, such as the package-private class returned by Collections.synchronizedCollection, the JDK maintainers had to override the default removeIf implementation and other methods like it to perform the necessary synchronization before invoking the default implementation. Preexisting collection implementations that were not part of the Java platform did not have the opportunity to make analogous changes in lockstep with the interface change, and some have yet to do so.
為了防止類似的 Java 庫實現(例如 `Collections.synchronizedCollection` 返回的包私有類)中發生這種情況,JDK 維護人員必須覆蓋默認的 removeIf 實現和其他類似的方法,以便在調用默認實現之前執行必要的同步。不屬于 Java 平臺的現有集合實現沒有機會與接口更改同步進行類似的更改,有些實現還沒有這樣做。
**In the presence of default methods, existing implementations of an interface may compile without error or warning but fail at runtime.** While not terribly common, this problem is not an isolated incident either. A handful of the methods added to the collections interfaces in Java 8 are known to be susceptible, and a handful of existing implementations are known to be affected.
在有默認方法的情況下,接口的現有實現可以在沒有錯誤或警告的情況下通過編譯,但是在運行時出錯。雖然這個問題并不常見,但也沒有那么罕見。已知 Java 8 中添加到集合接口的少數方法是易受影響的,會影響到現存的一部分實現。
Using default methods to add new methods to existing interfaces should be avoided unless the need is critical, in which case you should think long and hard about whether an existing interface implementation might be broken by your default method implementation. Default methods are, however, extremely useful for providing standard method implementations when an interface is created, to ease the task of implementing the interface (Item 20).
除非別無他法,否則應該避免使用默認方法向現有接口添加新方法,如果非要這么做,你應該仔細考慮現有接口實現是否可能被默認方法破壞。然而,在創建接口時,默認方法非常有助于提供標準方法實現,以減輕實現接口的任務量([Item-20](/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md))。
It is also worth noting that default methods were not designed to support removing methods from interfaces or changing the signatures of existing methods. Neither of these interface changes is possible without breaking existing clients.
同樣值得注意的是,默認方法的設計并不支持從接口中刪除方法或更改現有方法的簽名。你不能做出這些更改,除非破壞現有實現。
The moral is clear. Even though default methods are now a part of the Java platform, **it is still of the utmost importance to design interfaces with great care.** While default methods make it possible to add methods to existing interfaces, there is great risk in doing so. If an interface contains a minor flaw, it may irritate its users forever; if an interface is severely deficient, it may doom the API that contains it.
教訓顯而易見。盡管默認方法現在已經是 Java 平臺的一部分,但是謹慎地設計接口仍然是非常重要的。**雖然默認方法使向現有接口添加方法成為可能,但這樣做存在很大風險。** 如果一個接口包含一個小缺陷,它可能會永遠影響它的使用者;如果接口有嚴重缺陷,它可能會毀掉包含它的 API。
Therefore, it is critically important to test each new interface before you release it. Multiple programmers should implement each interface in different ways. At a minimum, you should aim for three diverse implementations. Equally important is to write multiple client programs that use instances of each new interface to perform various tasks. This will go a long way toward ensuring that each interface satisfies all of its intended uses. These steps will allow you to discover flaws in interfaces before they are released, when you can still correct them easily. **While it may be possible to correct some interface flaws after an interface is released, you cannot count on it.**
因此,在發布每個新接口之前對其進行測試非常重要。多個程序員應該以不同的方式測試每個接口。至少,你應該以三種不同的實現為目標。同樣重要的是編寫多個客戶端程序,用這些程序使用每個新接口的實例來執行各種任務。這將大大有助于確保每個接口滿足其所有預期用途。這些步驟將允許你在接口被發布之前發現它們的缺陷,而你仍然可以輕松地糾正它們。**雖然在接口被發布之后可以糾正一些接口缺陷,但是你不能指望這種方式。**
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-4/Chapter-4-Introduction.md)**
- **Previous Item(上一條目):[Item 20: Prefer interfaces to abstract classes(接口優于抽象類)](/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)**
- **Next Item(下一條目):[Item 22: Use interfaces only to define types(接口只用于定義類型)](/Chapter-4/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md)**
- Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
- Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)
- Item 2: Consider a builder when faced with many constructor parameters(在面對多個構造函數參數時,請考慮構建器)
- Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)
- Item 4: Enforce noninstantiability with a private constructor(用私有構造函數實施不可實例化)
- Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)
- Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
- Item 7: Eliminate obsolete object references(排除過時的對象引用)
- Item 8: Avoid finalizers and cleaners(避免使用終結器和清除器)
- Item 9: Prefer try with resources to try finally(使用 try-with-resources 優于 try-finally)
- Chapter 3. Methods Common to All Objects(對象的通用方法)
- Item 10: Obey the general contract when overriding equals(覆蓋 equals 方法時應遵守的約定)
- Item 11: Always override hashCode when you override equals(當覆蓋 equals 方法時,總要覆蓋 hashCode 方法)
- Item 12: Always override toString(始終覆蓋 toString 方法)
- Item 13: Override clone judiciously(明智地覆蓋 clone 方法)
- Item 14: Consider implementing Comparable(考慮實現 Comparable 接口)
- Chapter 4. Classes and Interfaces(類和接口)
- Item 15: Minimize the accessibility of classes and members(盡量減少類和成員的可訪問性)
- Item 16: In public classes use accessor methods not public fields(在公共類中,使用訪問器方法,而不是公共字段)
- Item 17: Minimize mutability(減少可變性)
- Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)
- Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用)
- Item 20: Prefer interfaces to abstract classes(接口優于抽象類)
- Item 21: Design interfaces for posterity(為后代設計接口)
- Item 22: Use interfaces only to define types(接口只用于定義類型)
- Item 23: Prefer class hierarchies to tagged classes(類層次結構優于帶標簽的類)
- Item 24: Favor static member classes over nonstatic(靜態成員類優于非靜態成員類)
- Item 25: Limit source files to a single top level class(源文件僅限有單個頂層類)
- Chapter 5. Generics(泛型)
- Item 26: Do not use raw types(不要使用原始類型)
- Item 27: Eliminate unchecked warnings(消除 unchecked 警告)
- Item 28: Prefer lists to arrays(list 優于數組)
- Item 29: Favor generic types(優先使用泛型)
- Item 30: Favor generic methods(優先使用泛型方法)
- Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
- Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)
- Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)
- Chapter 6. Enums and Annotations(枚舉和注解)
- Item 34: Use enums instead of int constants(用枚舉類型代替 int 常量)
- Item 35: Use instance fields instead of ordinals(使用實例字段替代序數)
- Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)
- Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
- Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)
- Item 39: Prefer annotations to naming patterns(注解優于命名模式)
- Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)
- Item 41: Use marker interfaces to define types(使用標記接口定義類型)
- Chapter 7. Lambdas and Streams(λ 表達式和流)
- Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
- Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)
- Item 44: Favor the use of standard functional interfaces(優先使用標準函數式接口)
- Item 45: Use streams judiciously(明智地使用流)
- Item 46: Prefer side effect free functions in streams(在流中使用無副作用的函數)
- Item 47: Prefer Collection to Stream as a return type(優先選擇 Collection 而不是流作為返回類型)
- Item 48: Use caution when making streams parallel(謹慎使用并行流)
- Chapter 8. Methods(方法)
- Item 49: Check parameters for validity(檢查參數的有效性)
- Item 50: Make defensive copies when needed(在需要時制作防御性副本)
- Item 51: Design method signatures carefully(仔細設計方法簽名)
- Item 52: Use overloading judiciously(明智地使用重載)
- Item 53: Use varargs judiciously(明智地使用可變參數)
- Item 54: Return empty collections or arrays, not nulls(返回空集合或數組,而不是 null)
- Item 55: Return optionals judiciously(明智地的返回 Optional)
- Item 56: Write doc comments for all exposed API elements(為所有公開的 API 元素編寫文檔注釋)
- Chapter 9. General Programming(通用程序設計)
- Item 57: Minimize the scope of local variables(將局部變量的作用域最小化)
- Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)
- Item 59: Know and use the libraries(了解并使用庫)
- Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)
- Item 61: Prefer primitive types to boxed primitives(基本數據類型優于包裝類)
- Item 62: Avoid strings where other types are more appropriate(其他類型更合適時應避免使用字符串)
- Item 63: Beware the performance of string concatenation(當心字符串連接引起的性能問題)
- Item 64: Refer to objects by their interfaces(通過接口引用對象)
- Item 65: Prefer interfaces to reflection(接口優于反射)
- Item 66: Use native methods judiciously(明智地使用本地方法)
- Item 67: Optimize judiciously(明智地進行優化)
- Item 68: Adhere to generally accepted naming conventions(遵守被廣泛認可的命名約定)
- Chapter 10. Exceptions(異常)
- Item 69: Use exceptions only for exceptional conditions(僅在確有異常條件下使用異常)
- Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors(對可恢復情況使用 checked 異常,對編程錯誤使用運行時異常)
- Item 71: Avoid unnecessary use of checked exceptions(避免不必要地使用 checked 異常)
- Item 72: Favor the use of standard exceptions(鼓勵復用標準異常)
- Item 73: Throw exceptions appropriate to the abstraction(拋出能用抽象解釋的異常)
- Item 74: Document all exceptions thrown by each method(為每個方法記錄會拋出的所有異常)
- Item 75: Include failure capture information in detail messages(異常詳細消息中應包含捕獲失敗的信息)
- Item 76: Strive for failure atomicity(盡力保證故障原子性)
- Item 77: Don’t ignore exceptions(不要忽略異常)
- Chapter 11. Concurrency(并發)
- Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)
- Item 79: Avoid excessive synchronization(避免過度同步)
- Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)
- Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
- Item 82: Document thread safety(文檔應包含線程安全屬性)
- Item 83: Use lazy initialization judiciously(明智地使用延遲初始化)
- Item 84: Don’t depend on the thread scheduler(不要依賴線程調度器)
- Chapter 12. Serialization(序列化)
- Item 85: Prefer alternatives to Java serialization(優先選擇 Java 序列化的替代方案)
- Item 86: Implement Serializable with great caution(非常謹慎地實現 Serializable)
- Item 87: Consider using a custom serialized form(考慮使用自定義序列化形式)
- Item 88: Write readObject methods defensively(防御性地編寫 readObject 方法)
- Item 89: For instance control, prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)
- Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例)
