## Chapter 5. Generics(泛型)
### Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
As noted in Item 28, parameterized types are invariant. In other words, for any two distinct types `Type1` and `Type2`, `List<Type1>` is neither a subtype nor a supertype of `List<Type2>`. Although it is counterintuitive that `List<String>` is not a subtype of `List<Object>`, it really does make sense. You can put any object into a `List<Object>`, but you can put only strings into a `List<String>`. Since a `List<String>` can’t do everything a `List<Object>` can, it isn’t a subtype (by the Liskov substitution principal, Item 10).
如 [Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 所示,參數化類型是不可變的。換句話說,對于任意兩種不同類型 `Type1` 和 `Type2`,`List<Type1>` 既不是 `List<Type2>` 的子類型,也不是它的父類。雖然 `List<String>` 不是 `List<Object>` 的子類型,這和習慣的直覺不符,但它確實有意義。你可以將任何對象放入 `List<Object>`,但只能將字符串放入 `List<String>`。因為 `List<String>` 不能做 `List<Object>` 能做的所有事情,所以它不是子類型(可通過 Liskov 替換原則來理解這一點,[Item-10](/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md))。
**譯注:里氏替換原則(Liskov Substitution Principle,LSP)面向對象設計的基本原則之一。里氏替換原則指出:任何父類可以出現的地方,子類一定可以出現。LSP 是繼承復用的基石,只有當衍生類可以替換掉父類,軟件單位的功能不受到影響時,父類才能真正被復用,而衍生類也能夠在父類的基礎上增加新的行為。**
Sometimes you need more flexibility than invariant typing can provide. Consider the Stack class from Item 29. To refresh your memory, here is its public API:
有時你需要獲得比不可變類型更多的靈活性。考慮 [Item-29](/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) 中的堆棧類。以下是它的公共 API:
```
public class Stack<E> {
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
}
```
Suppose we want to add a method that takes a sequence of elements and pushes them all onto the stack. Here’s a first attempt:
假設我們想添加一個方法,該方法接受一系列元素并將它們全部推入堆棧。這是第一次嘗試:
```
// pushAll method without wildcard type - deficient!
public void pushAll(Iterable<E> src) {
for (E e : src)
push(e);
}
```
This method compiles cleanly, but it isn’t entirely satisfactory. If the element type of the `Iterable src` exactly matches that of the stack, it works fine. But suppose you have a `Stack<Number>` and you invoke `push(intVal)`, where `intVal` is of type `Integer`. This works because `Integer` is a subtype of `Number`. So logically, it seems that this should work, too:
該方法能夠正確編譯,但并不完全令人滿意。如果 `Iterable src` 的元素類型與堆棧的元素類型完全匹配,那么它正常工作。但是假設你有一個 `Stack<Number>`,并且調用 `push(intVal)`,其中 `intVal` 的類型是 `Integer`。這是可行的,因為 `Integer` 是 `Number` 的子類型。因此,從邏輯上講,這似乎也應該奏效:
```
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);
```
If you try it, however, you’ll get this error message because parameterized types are invariant:
但是,如果你嘗試一下,將會得到這個錯誤消息,因為參數化類型是不可變的:
```
StackTest.java:7: error: incompatible types: Iterable<Integer>
cannot be converted to Iterable<Number>
numberStack.pushAll(integers);
^
```
Luckily, there’s a way out. The language provides a special kind of parameterized type call a bounded wildcard type to deal with situations like this. The type of the input parameter to pushAll should not be “Iterable of E” but “Iterable of some subtype of E,” and there is a wildcard type that means precisely that: Iterable<? extends E>. (The use of the keyword extends is slightly misleading: recall from Item 29 that subtype is defined so that every type is a subtype of itself, even though it does not extend itself.) Let’s modify pushAll to use this type:
幸運的是,有一種解決方法。Java 提供了一種特殊的參數化類型,`有界通配符類型`來處理這種情況。pushAll 的輸入參數的類型不應該是「E 的 Iterable 接口」,而應該是「E 的某個子類型的 Iterable 接口」,并且有一個通配符類型,它的確切含義是:`Iterable<? extends E>`(關鍵字 extends 的使用稍微有些誤導:回想一下 [Item-29](/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md),定義了子類型,以便每個類型都是其本身的子類型,即使它沒有擴展自己。)讓我們修改 pushAll 來使用這種類型:
```
// Wildcard type for a parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
```
With this change, not only does Stack compile cleanly, but so does the client code that wouldn’t compile with the original pushAll declaration. Because Stack and its client compile cleanly, you know that everything is typesafe. Now suppose you want to write a popAll method to go with pushAll. The popAll method pops each element off the stack and adds the elements to the given collection. Here’s how a first attempt at writing the popAll method might look:
更改之后,不僅 Stack 可以正確編譯,而且不能用原始 pushAll 聲明編譯的客戶端代碼也可以正確編譯。因為 Stack 和它的客戶端可以正確編譯,所以你知道所有東西都是類型安全的。現在假設你想編寫一個與 pushAll 一起使用的 popAll 方法。popAll 方法將每個元素從堆棧中彈出,并將這些元素添加到給定的集合中。下面是編寫 popAll 方法的第一次嘗試:
```
// popAll method without wildcard type - deficient!
public void popAll(Collection<E> dst) {
while (!isEmpty())
dst.add(pop());
}
```
Again, this compiles cleanly and works fine if the element type of the destination collection exactly matches that of the stack. But again, it isn’t entirely satisfactory. Suppose you have a `Stack<Number>` and variable of type Object. If you pop an element from the stack and store it in the variable, it compiles and runs without error. So shouldn’t you be able to do this, too?
同樣,如果目標集合的元素類型與堆棧的元素類型完全匹配,那么這種方法可以很好地編譯。但這也不是完全令人滿意。假設你有一個 `Stack<Number>` 和 Object 類型的變量。如果從堆棧中取出一個元素并將其存儲在變量中,那么它將編譯并運行,不會出錯。所以你不能也這樣做嗎?
```
Stack<Number> numberStack = new Stack<Number>();
Collection<Object> objects = ... ;
numberStack.popAll(objects);
```
If you try to compile this client code against the version of popAll shown earlier, you’ll get an error very similar to the one that we got with our first version of pushAll: `Collection<Object>` is not a subtype of `Collection<Number>`. Once again, wildcard types provide a way out. The type of the input parameter to popAll should not be “collection of E” but “collection of some supertype of E” (where supertype is defined such that E is a supertype of itself [JLS, 4.10]). Again, there is a wildcard type that means precisely that: Collection<? super E>. Let’s modify popAll to use it:
如果你嘗試根據前面顯示的 popAll 版本編譯此客戶端代碼,你將得到一個與第一個版本的 pushAll 非常相似的錯誤:`Collection<Object>`不是 `Collection<Number>` 的子類型。同樣,通配符類型提供解決方法。popAll 的輸入參數的類型不應該是「E 的集合」,而應該是「E 的某個超類型的集合」(其中的超類型定義為 E 本身是一個超類型[JLS, 4.10])。同樣,有一個通配符類型,它的確切含義是:`Collection<? super E>`。讓我們修改 popAll 來使用它:
```
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
```
With this change, both Stack and the client code compile cleanly.
通過此更改,Stack 類和客戶端代碼都可以正確編譯。
The lesson is clear. **For maximum flexibility, use wildcard types on input parameters that represent producers or consumers.** If an input parameter is both a producer and a consumer, then wildcard types will do you no good: you need an exact type match, which is what you get without any wildcards. Here is a mnemonic to help you remember which wildcard type to use:
教訓是清楚的。為了獲得最大的靈活性,應在表示生產者或消費者的輸入參數上使用通配符類型。如果輸入參數既是生產者又是消費者,那么通配符類型對你沒有任何好處:你需要一個精確的類型匹配,這就是在沒有通配符的情況下得到的結果。這里有一個助記符幫助你記住使用哪種通配符類型:
**PECS stands for producer-extends, consumer-super.**
PECS 表示生產者應使用 extends,消費者應使用 super。
In other words, if a parameterized type represents a T producer, use `<? extends T>`; if it represents a T consumer, use `<? super T>`. In our Stack example, pushAll’s src parameter produces E instances for use by the Stack, so the appropriate type for src is `Iterable<? extends E>`; popAll’s dst parameter consumes E instances from the Stack, so the appropriate type for dst is `Collection<? super E>`. The PECS mnemonic captures the fundamental principle that guides the use of wild-card types. Naftalin and Wadler call it the Get and Put Principle [Naftalin07, 2.4].
換句話說,如果參數化類型表示 T 生成器,則使用 `<? extends T>`;如果它表示一個 T 消費者,則使用 `<? super T>`。在我們的 Stack 示例中,pushAll 的 src 參數生成 E 的實例供 Stack 使用,因此 src 的適當類型是 `Iterable<? extends E>`;popAll 的 dst 參數使用 Stack 中的 E 實例,因此適合 dst 的類型是 `Collection<? super E>`。PECS 助記符捕獲了指導通配符類型使用的基本原則。Naftalin 和 Wadler 稱之為 Get and Put 原則[Naftalin07, 2.4]。
With this mnemonic in mind, let’s take a look at some method and constructor declarations from previous items in this chapter. The Chooser constructor in Item 28 has this declaration:
記住這個助記符后,再讓我們看一看本章前面提及的一些方法和構造函數聲明。[Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 中的 Chooser 構造函數有如下聲明:
```
public Chooser(Collection<T> choices)
```
This constructor uses the collection choices only to produce values of type T (and stores them for later use), so its declaration should use a wildcard type that **extends T.** Here’s the resulting constructor declaration:
這個構造函數只使用集合選項來生成類型 T 的值(并存儲它們以供以后使用),因此它的聲明應該使用擴展 T 的通配符類型 **extends T**。下面是生成的構造函數聲明:
```
// Wildcard type for parameter that serves as an T producer
public Chooser(Collection<? extends T> choices)
```
And would this change make any difference in practice? Yes, it would. Suppose you have a `List<Integer>`, and you want to pass it in to the constructor for a Chooser<Number>. This would not compile with the original declaration, but it does once you add the bounded wildcard type to the declaration.
這種改變在實踐中會有什么不同嗎?是的,它會。假設你有一個 `List<Integer>`,并且希望將其傳遞給 `Chooser<Number>` 的構造函數。這不會與原始聲明一起編譯,但是一旦你將有界通配符類型添加到聲明中,它就會編譯。
Now let’s look at the union method from Item 30. Here is the declaration:
現在讓我們看看 [Item-30](/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) 中的 union 方法。以下是聲明:
```
public static <E> Set<E> union(Set<E> s1, Set<E> s2)
```
Both parameters, s1 and s2, are E producers, so the PECS mnemonic tells us that the declaration should be as follows:
參數 s1 和 s2 都是 E 的生產者,因此 PECS 助記符告訴我們聲明應該如下:
```
public static <E> Set<E> union(Set<? extends E> s1,Set<? extends E> s2)
```
Note that the return type is still `Set<E>`. **Do not use bounded wildcard types as return types.** Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code. With the revised declaration, this code will compile cleanly:
注意,返回類型仍然設置為 `Set<E>`。**不要使用有界通配符類型作為返回類型。** 它將強制用戶在客戶端代碼中使用通配符類型,而不是為用戶提供額外的靈活性。經修訂后的聲明可正確編譯以下代碼:
```
Set<Integer> integers = Set.of(1, 3, 5);
Set<Double> doubles = Set.of(2.0, 4.0, 6.0);
Set<Number> numbers = union(integers, doubles);
```
Properly used, wildcard types are nearly invisible to the users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. **If the user of a class has to think about wildcard types, there is probably something wrong with its API.**
如果使用得當,通配符類型對于類的用戶幾乎是不可見的。它們讓方法接受它們應該接受的參數,拒絕應該拒絕的參數。**如果類的用戶必須考慮通配符類型,那么它的 API 可能有問題。**
Prior to Java 8, the type inference rules were not clever enough to handle the previous code fragment, which requires the compiler to use the contextually specified return type (or target type) to infer the type of E. The target type of the union invocation shown earlier is `Set<Number>`. If you try to compile the fragment in an earlier version of Java (with an appropriate replacement for the Set.of factory), you’ll get a long, convoluted error message like this:
在 Java 8 之前,類型推斷規則還不足以處理前面的代碼片段,這要求編譯器使用上下文指定的返回類型(或目標類型)來推斷 E 的類型。前面顯示的 union 調用的目標類型設置為 `Set<Number>` 如果你嘗試在 Java 的早期版本中編譯該片段(使用 Set.of factory 的適當替代),你將得到一條長而復雜的錯誤消息,如下所示:
```
Union.java:14: error: incompatible types
Set<Number> numbers = union(integers, doubles);
^ required: Set<Number>
found: Set<INT#1>
where INT#1,INT#2 are intersection types:
INT#1 extends Number,Comparable<? extends INT#2>
INT#2 extends Number,Comparable<?>
```
Luckily there is a way to deal with this sort of error. If the compiler doesn’t infer the correct type, you can always tell it what type to use with an explicit type argument [JLS, 15.12]. Even prior to the introduction of target typing in Java 8, this isn’t something that you had to do often, which is good because explicit type arguments aren’t very pretty. With the addition of an explicit type argument, as shown here, the code fragment compiles cleanly in versions prior to Java 8:
幸運的是,有一種方法可以處理這種錯誤。如果編譯器沒有推斷出正確的類型,你總是可以告訴它使用顯式類型參數[JLS, 15.12]使用什么類型。即使在 Java 8 中引入目標類型之前,這也不是必須經常做的事情,這很好,因為顯式類型參數不是很漂亮。通過添加顯式類型參數,如下所示,代碼片段可以在 Java 8 之前的版本中正確編譯:
```
// Explicit type parameter - required prior to Java 8
Set<Number> numbers = Union.<Number>union(integers, doubles);
```
Next let’s turn our attention to the max method in Item 30. Here is the original declaration:
接下來讓我們將注意力轉到 [Item-30](/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) 中的 max 方法。以下是原始聲明:
```
public static <T extends Comparable<T>> T max(List<T> list)
```
Here is a revised declaration that uses wildcard types:
下面是使用通配符類型的修正聲明:
```
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
```
To get the revised declaration from the original, we applied the PECS heuristic twice. The straightforward application is to the parameter list. It produces T instances, so we change the type from `List<T>` to `List<? extends T>`. The tricky application is to the type parameter T. This is the first time we’ve seen a wildcard applied to a type parameter. Originally, T was specified to extend `Comparable<T>`, but a comparable of T consumes T instances (and produces integers indicating order relations). Therefore, the parameterized type `Comparable<T>` is replaced by the bounded wildcard type `Comparable<? super T>`. Comparables are always consumers, so you should generally **use `Comparable<? super T>` in preference to `Comparable<T>`.** The same is true of comparators; therefore, you should generally **use `Comparator<? super T>` in preference to `Comparator<T>`.**
為了從原始聲明中得到修改后的聲明,我們兩次應用了 PECS 啟發式。直接的應用程序是參數列表。它生成 T 的實例,所以我們將類型從 `List<T>` 更改為 `List<? extends T>`。復雜的應用是類型參數 T。這是我們第一次看到通配符應用于類型參數。最初,T 被指定為擴展 `Comparable<T>`,但是 T 的 Comparable 消費 T 實例(并生成指示順序關系的整數)。因此,將參數化類型 `Comparable<T>` 替換為有界通配符類型 `Comparable<? super T>`,Comparables 始終是消費者,所以一般應**優先使用 `Comparable<? super T>` 而不是 `Comparable<T>`**,比較器也是如此;因此,通常應該**優先使用 `Comparator<? super T>` 而不是 `Comparator<T>`。**
The revised max declaration is probably the most complex method declaration in this book. Does the added complexity really buy you anything? Again, it does. Here is a simple example of a list that would be excluded by the original declaration but is permitted by the revised one:
修訂后的 max 聲明可能是本書中最復雜的方法聲明。增加的復雜性真的能給你帶來什么好處嗎?是的,它再次生效。下面是一個簡單的列表案例,它在原來的聲明中不允許使用,但經訂正的聲明允許:
```
List<ScheduledFuture<?>> scheduledFutures = ... ;
```
The reason that you can’t apply the original method declaration to this list is that ScheduledFuture does not implement `Comparable<ScheduledFuture>`. Instead, it is a subinterface of Delayed, which extends `Comparable<Delayed>`. In other words, a ScheduledFuture instance isn’t merely comparable to other ScheduledFuture instances; it is comparable to any Delayed instance, and that’s enough to cause the original declaration to reject it. More generally, the wildcard is required to support types that do not implement Comparable (or Comparator) directly but extend a type that does.
不能將原始方法聲明應用于此列表的原因是 ScheduledFuture 沒有實現 `Comparable<ScheduledFuture>`。相反,它是 Delayed 的一個子接口,擴展了 `Comparable<Delayed>`。換句話說,ScheduledFuture 的實例不僅僅可以與其他 ScheduledFuture 實例進行比較;它可以與任何 Delayed 實例相比較,這足以導致初始聲明時被拒絕。更通俗來說,通配符用于支持不直接實現 Comparable(或 Comparator)但擴展了實現 Comparable(或 Comparator)的類型的類型。
There is one more wildcard-related topic that bears discussing. There is a duality between type parameters and wildcards, and many methods can be declared using one or the other. For example, here are two possible declarations for a static method to swap two indexed items in a list. The first uses an unbounded type parameter (Item 30) and the second an unbounded wildcard:
還有一個與通配符相關的主題值得討論。類型參數和通配符之間存在對偶性,可以使用其中一種方法聲明許多方法。例如,下面是靜態方法的兩種可能聲明,用于交換列表中的兩個索引項。第一個使用無界類型參數([Item-30](/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md)),第二個使用無界通配符:
```
// Two possible declarations for the swap method
public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);
```
Which of these two declarations is preferable, and why? In a public API, the second is better because it’s simpler. You pass in a list—any list—and the method swaps the indexed elements. There is no type parameter to worry about. As a rule, **if a type parameter appears only once in a method declaration, replace it with a wildcard.** If it’s an unbounded type parameter, replace it with an unbounded wildcard; if it’s a bounded type parameter, replace it with a bounded wildcard.
這兩個聲明中哪個更好,為什么?在公共 API 中第二個更好,因為它更簡單。傳入一個列表(任意列表),該方法交換索引元素。不需要擔心類型參數。通常,如果類型參數在方法聲明中只出現一次,則用通配符替換它。**如果它是一個無界類型參數,用一個無界通配符替換它;** 如果它是有界類型參數,則用有界通配符替換它。
There’s one problem with the second declaration for swap. The straightforward implementation won’t compile:
交換的第二個聲明有一個問題。這個簡單的實現無法編譯:
```
public static void swap(List<?> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
```
Trying to compile it produces this less-than-helpful error message:
試圖編譯它會產生一個不太有用的錯誤消息:
```
Swap.java:5: error: incompatible types: Object cannot be
converted to CAP#1
list.set(i, list.set(j, list.get(i)));
^ where CAP#1
is a fresh type-variable: CAP#1 extends Object from capture of ?
```
It doesn’t seem right that we can’t put an element back into the list that we just took it out of. The problem is that the type of list is `List<?>`, and you can’t put any value except null into a `List<?>`. Fortunately, there is a way to implement this method without resorting to an unsafe cast or a raw type. The idea is to write a private helper method to capture the wildcard type. The helper method must be a generic method in order to capture the type. Here’s how it looks:
我們不能把一個元素放回剛剛取出的列表中,這看起來是不正確的。問題是 list 的類型是 `List<?>`,你不能在 `List<?>` 中放入除 null 以外的任何值。幸運的是,有一種方法可以實現,而無需求助于不安全的強制轉換或原始類型。其思想是編寫一個私有助手方法來捕獲通配符類型。為了捕獲類型,helper 方法必須是泛型方法。它看起來是這樣的:
```
public static void swap(List<?> list, int i, int j) {
swapHelper(list, i, j);
}
// Private helper method for wildcard capture
private static <E> void swapHelper(List<E> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
```
The swapHelper method knows that list is a `List<E>`. Therefore, it knows that any value it gets out of this list is of type E and that it’s safe to put any value of type E into the list. This slightly convoluted implementation of swap compiles cleanly. It allows us to export the nice wildcard-based declaration, while taking advantage of the more complex generic method internally. Clients of the swap method don’t have to confront the more complex swapHelper declaration, but they do benefit from it. It is worth noting that the helper method has precisely the signature that we dismissed as too complex for the public method.
swapHelper 方法知道 list 是一個 `List<E>`。因此,它知道它從這個列表中得到的任何值都是 E 類型的,并且將 E 類型的任何值放入這個列表中都是安全的。這個稍微復雜的實現能夠正確編譯。它允許我們導出基于 通配符的聲明,同時在內部利用更復雜的泛型方法。swap 方法的客戶端不必面對更復雜的 swapHelper 聲明,但它們確實從中受益。值得注意的是,helper 方法具有我們認為對于公共方法過于復雜而忽略的簽名。
In summary, using wildcard types in your APIs, while tricky, makes the APIs far more flexible. If you write a library that will be widely used, the proper use of wildcard types should be considered mandatory. Remember the basic rule: producer-extends, consumer-super (PECS). Also remember that all comparables and comparators are consumers.
總之,在 API 中使用通配符類型雖然很棘手,但可以使其更加靈活。如果你編寫的庫將被廣泛使用,則必須考慮通配符類型的正確使用。記住基本規則:生產者使用 extends,消費者使用 super(PECS)。還要記住,所有的 comparable 和 comparator 都是消費者。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-5/Chapter-5-Introduction.md)**
- **Previous Item(上一條目):[Item 30: Favor generic methods(優先使用泛型方法)](/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md)**
- **Next Item(下一條目):[Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)](/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.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(考慮以序列化代理代替序列化實例)