## Chapter 8. Methods(方法)
### Item 49: Check parameters for validity(檢查參數的有效性)
Most methods and constructors have some restrictions on what values may be passed into their parameters. For example, it is not uncommon that index values must be non-negative and object references must be non-null. You should clearly document all such restrictions and enforce them with checks at the beginning of the method body. This is a special case of the general principle that you should attempt to detect errors as soon as possible after they occur. Failing to do so makes it less likely that an error will be detected and makes it harder to determine the source of an error once it has been detected.
大多數方法和構造函數都對傳遞給它們的參數值有一些限制。例如,索引值必須是非負的,對象引用必須是非空的,這種情況并不少見。你應該清楚地在文檔中記錄所有這些限制,并在方法主體的開頭使用檢查來實施它們。你應該在錯誤發生后盡快找到它們,這是一般原則。如果不這樣做,就不太可能檢測到錯誤,而且即使檢測到錯誤,確定錯誤的來源也很難。
If an invalid parameter value is passed to a method and the method checks its parameters before execution, it will fail quickly and cleanly with an appropriate exception. If the method fails to check its parameters, several things could happen. The method could fail with a confusing exception in the midst of processing. Worse, the method could return normally but silently compute the wrong result. Worst of all, the method could return normally but leave some object in a compromised state, causing an error at some unrelated point in the code at some undetermined time in the future. In other words, failure to validate parameters, can result in a violation of failure atomicity (Item 76).
如果一個無效的參數值被傳遞給一個方法,如果該方法在執行之前會檢查它的參數,那么這個過程將迅速失敗,并引發適當的異常。如果方法未能檢查其參數,可能會發生以下幾件事。該方法可能會在處理過程中出現令人困惑的異常而失敗。更糟的是,該方法可以正常返回,但會靜默計算錯誤的結果。最糟糕的是,該方法可以正常返回,但會使某個對象處于隱患狀態,從而在將來某個不確定的時間在代碼中某個不相關的位置上導致錯誤。換句話說,如果沒有驗證參數,可能會違反故障原子性([Item-76](/Chapter-10/Chapter-10-Item-76-Strive-for-failure-atomicity.md))。
For public and protected methods, use the Javadoc @throws tag to document the exception that will be thrown if a restriction on parameter values is violated (Item 74). Typically, the resulting exception will be IllegalArgumentException, IndexOutOfBoundsException, or NullPointerException (Item 72). Once you’ve documented the restrictions on a method’s parameters and you’ve documented the exceptions that will be thrown if these restrictions are violated, it is a simple matter to enforce the restrictions. Here’s a typical example:
對于公共方法和受保護的方法,如果在方法說明使用 Javadoc 的 `@throws` 標簽記錄異常,表明如果違反了對參數值的限制,將會引發該異常([Item-74](/Chapter-10/Chapter-10-Item-74-Document-all-exceptions-thrown-by-each-method.md))。通常,生成的異常將是 IllegalArgumentException、IndexOutOfBoundsException 或 NullPointerException([Item-72](/Chapter-10/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md))。一旦你在文檔中記錄了方法參數上的限制,并且記錄了如果違反這些限制將引發的異常,那么實施這些限制就很簡單了。這里有一個典型的例子:
```
/**
* Returns a BigInteger whose value is (this mod m). This method
* differs from the remainder method in that it always returns a
* non-negative BigInteger.
**
@param m the modulus, which must be positive
* @return this mod m
* @throws ArithmeticException if m is less than or equal to 0
*/
public BigInteger mod(BigInteger m) {
if (m.signum() <= 0)
throw new ArithmeticException("Modulus <= 0: " + m);
... // Do the computation
}
```
Note that the doc comment does not say “mod throws NullPointerException if m is null,” even though the method does exactly that, as a byproduct of invoking m.signum(). This exception is documented in the class-level doc comment for the enclosing BigInteger class. The classlevel comment applies to all parameters in all of the class’s public methods. This is a good way to avoid the clutter of documenting every NullPointerException on every method individually. It may be combined with the use of @Nullable or a similar annotation to indicate that a particular parameter may be null, but this practice is not standard, and multiple annotations are in use for this purpose.
注意,文檔注釋并沒有說「如果 m 為空,mod 將拋出NullPointerException」,盡管方法確實是這樣做的,這是調用 `m.signum()` 的副產品。這個異常記錄在類級別的文檔注釋中,用于包含 BigInteger 類。類級別注釋適用于類的所有公共方法中的所有參數。這是避免在每個方法上分別記錄每個 NullPointerException 而造成混亂的好方法。它可以與 `@Nullable` 或類似的注釋結合使用,以指示某個特定參數可能為 null,但這種做法并不標準,為此使用了多個注釋。
**The Objects.requireNonNull method, added in Java 7, is flexible and convenient, so there’s no reason to perform null checks manually anymore.** You can specify your own exception detail message if you wish. The method returns its input, so you can perform a null check at the same time as you use a value:
**在 Java 7 中添加的 `Objects.requireNonNull` 方法非常靈活和方便,因此不再需要手動執行空檢查。** 如果愿意,可以指定自己的異常詳細信息。該方法返回它的輸入,所以你可以執行一個空檢查,同時你使用一個值:
```
// Inline use of Java's null-checking facility
this.strategy = Objects.requireNonNull(strategy, "strategy");
```
You can also ignore the return value and use Objects.requireNonNull as a freestanding null check where that suits your needs.
你還可以忽略返回值并使用 `Objects.requireNonNull` 作為一個獨立的 null 檢查來滿足你的需要。
In Java 9, a range-checking facility was added to java.util.Objects. This facility consists of three methods: checkFromIndexSize, checkFromToIndex, and checkIndex. This facility is not as flexible as the null-checking method. It doesn’t let you specify your own exception detail message, and it is designed solely for use on list and array indices. It does not handle closed ranges (which contain both of their endpoints). But if it does what you need, it’s a useful convenience.
在 Java 9 中,范圍檢查功能被添加到 `java.util.Objects` 中。這個功能由三個方法組成:checkFromIndexSize、checkFromToIndex 和 checkIndex。這個工具不如空檢查方法靈活。它不允許你指定自己的異常詳細信息,而且它僅用于 List 和數組索引。它不處理封閉范圍(包含兩個端點)。但如果它滿足你的需求,它仍是一個有用的工具。
For an unexported method, you, as the package author, control the circumstances under which the method is called, so you can and should ensure that only valid parameter values are ever passed in. Therefore, nonpublic methods can check their parameters using assertions, as shown below:
對于未導出的方法,作為包的作者,你應該定制方法調用的環境,因此你可以并且應該確保只傳遞有效的參數值。因此,非公共方法可以使用斷言檢查它們的參數,如下所示:
```
// Private helper function for a recursive sort
private static void sort(long a[], int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length - offset;
... // Do the computation
}
```
In essence, these assertions are claims that the asserted condition will be true, regardless of how the enclosing package is used by its clients. Unlike normal validity checks, assertions throw AssertionError if they fail. And unlike normal validity checks, they have no effect and essentially no cost unless you enable them, which you do by passing the -ea (or -enableassertions) flag to the java command. For more information on assertions, see the tutorial [Asserts].
從本質上說,這些斷言在聲明時,條件將為 true,而不管其客戶端如何使用所包含的包。與普通的有效性檢查不同,如果斷言失敗,則會拋出 AssertionError。與普通的有效性檢查不同,如果斷言沒有起到作用,本質上不存在成本,除非你啟用它們,你可以通過將 `-ea`( 或 `-enableassertion`)標志傳遞給 java 命令來啟用它們。有關斷言的更多信息,請參見教程 [Asserts]。
It is particularly important to check the validity of parameters that are not used by a method, but stored for later use. For example, consider the static factory method on page 101, which takes an int array and returns a List view of the array. If a client were to pass in null, the method would throw a NullPointerException because the method has an explicit check (the call to Objects.requireNonNull). Had the check been omitted, the method would return a reference to a newly created List instance that would throw a NullPointerException as soon as a client attempted to use it. By that time, the origin of the List instance might be difficult to determine, which could greatly complicate the task of debugging.
特別重要的是,應檢查那些不是由方法使用,而是存儲起來供以后使用的參數的有效性。例如,考慮第 101 頁中的靜態工廠方法,它接受一個 int 數組并返回數組的 List 視圖。如果客戶端傳入 null,該方法將拋出 NullPointerException,因為該方法具有顯式檢查(調用 `Objects.requireNonNull`)。如果省略了檢查,該方法將返回對新創建的 List 實例的引用,該實例將在客戶端試圖使用它時拋出 NullPointerException。到那時,List 實例的起源可能很難確定,這可能會使調試任務變得非常復雜。
Constructors represent a special case of the principle that you should check the validity of parameters that are to be stored away for later use. It is critical to check the validity of constructor parameters to prevent the construction of an object that violates its class invariants.
構造函數代表了一種特殊的情況,即,你應該檢查要存儲起來供以后使用的參數的有效性。檢查構造函數參數的有效性對于防止構造生成實例對象時,違背類的對象的不變性非常重要。
There are exceptions to the rule that you should explicitly check a method’s parameters before performing its computation. An important exception is the case in which the validity check would be expensive or impractical and the check is performed implicitly in the process of doing the computation. For example, consider a method that sorts a list of objects, such as Collections.sort(List). All of the objects in the list must be mutually comparable. In the process of sorting the list, every object in the list will be compared to some other object in the list. If the objects aren’t mutually comparable, one of these comparisons will throw a ClassCastException, which is exactly what the sort method should do. Therefore, there would be little point in checking ahead of time that the elements in the list were mutually comparable. Note, however, that indiscriminate reliance on implicit validity checks can result in the loss of failure atomicity (Item 76).
在執行方法的計算任務之前,應該顯式地檢查方法的參數,這條規則也有例外。一個重要的例外是有效性檢查成本較高或不切實際,或者檢查是在計算過程中隱式執行了。例如,考慮一個為對象 List 排序的方法,比如 `Collections.sort(List)`。List 中的所有對象必須相互比較。在對 List 排序的過程中,List 中的每個對象都會與列表中的其他對象進行比較。如果對象不能相互比較,將拋出 ClassCastException,這正是 sort 方法應該做的。因此,沒有必要預先檢查列表中的元素是否具有可比性。但是,請注意,不加區別地依賴隱式有效性檢查可能導致失敗原子性的丟失([Item-76](/Chapter-10/Chapter-10-Item-76-Strive-for-failure-atomicity.md))。
Occasionally, a computation implicitly performs a required validity check but throws the wrong exception if the check fails. In other words, the exception that the computation would naturally throw as the result of an invalid parameter value doesn’t match the exception that the method is documented to throw. Under these circumstances, you should use the exception translation idiom, described in Item 73, to translate the natural exception into the correct one.
有時,計算任務會隱式地執行所需的有效性檢查,但如果檢查失敗,則拋出錯誤的異常。換句話說,計算任務由于無效參數值拋出的異常,與文檔中記錄的方法要拋出的異常不匹配。在這種情況下,你應該使用 [Item-73](/Chapter-10/Chapter-10-Item-73-Throw-exceptions-appropriate-to-the-abstraction.md) 中描述的異常轉譯技術來將計算任務拋出的異常轉換為正確的異常。
Do not infer from this item that arbitrary restrictions on parameters are a good thing. On the contrary, you should design methods to be as general as it is practical to make them. The fewer restrictions that you place on parameters, the better, assuming the method can do something reasonable with all of the parameter values that it accepts. Often, however, some restrictions are intrinsic to the abstraction being implemented.
不要從本條目推斷出:對參數的任意限制都是一件好事。相反,你應該把方法設計得既通用又實用。對參數施加的限制越少越好,假設該方法可以對它所接受的所有參數值進行合理的處理。然而,一些限制常常是實現抽象的內在限制。
To summarize, each time you write a method or constructor, you should think about what restrictions exist on its parameters. You should document these restrictions and enforce them with explicit checks at the beginning of the method body. It is important to get into the habit of doing this. The modest work that it entails will be paid back with interest the first time a validity check fails.
總而言之,每次編寫方法或構造函數時,都應該考慮參數存在哪些限制。你應該在文檔中記錄這些限制,并在方法主體的開頭顯式地檢查。養成這樣的習慣是很重要的。它所涉及的這一少量工作及其所花費的時間,將在有效性檢查出現第一次失敗時連本帶利地償還。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-8/Chapter-8-Introduction.md)**
- **Next Item(下一條目):[Item 50: Make defensive copies when needed(在需要時制作防御性副本)](/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.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(考慮以序列化代理代替序列化實例)