## Chapter 5. Generics(泛型)
### Item 28: Prefer lists to arrays(list 优于数组)
Arrays differ from generic types in two important ways. First, arrays are covariant. This scary-sounding word means simply that if Sub is a subtype of Super, then the array type Sub[] is a subtype of the array type Super[]. Generics, by contrast, are invariant: for any two distinct types Type1 and Type2, `List<Type1>` is neither a subtype nor a supertype of `List<Type2>` [JLS, 4.10; Naftalin07, 2.5]. You might think this means that generics are deficient, but arguably(可能,大概) it is arrays that are deficient. This code fragment is legal:
数组与泛型有两个重要区别。首先,数组是协变的。这个听起来很吓人的单词的意思很简单,如果 Sub 是 Super 的一个子类型,那么数组类型 Sub[] 就是数组类型 Super[] 的一个子类型。相比之下,泛型是不变的:对于任何两个不同类型 Type1 和 Type2,`List<Type1>` 既不是 `List<Type2>` 的子类型,也不是 `List<Type2>` 的超类型 [JLS, 4.10; Naftalin07, 2.5]。你可能认为这意味着泛型是有缺陷的,但可以说数组才是有缺陷的。这段代码是合法的:
```
// Fails at runtime!
Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
```
but this one is not:
但这一段代码就不是:
```
// Won't compile!
List<Object> ol = new ArrayList<Long>(); // Incompatible types
ol.add("I don't fit in");
```
Either way you can’t put a String into a Long container, but with an array you find out that you’ve made a mistake at runtime; with a list, you find out at compile time. Of course, you’d rather find out at compile time.
两种方法都不能将 String 放入 Long 容器,但使用数组,你会得到一个运行时错误;使用 list,你可以在编译时发现问题。当然,你更希望在编译时找到问题。
The second major difference between arrays and generics is that arrays are reified [JLS, 4.7]. This means that arrays know and enforce their element type at runtime. As noted earlier, if you try to put a String into an array of Long, you’ll get an ArrayStoreException. Generics, by contrast, are implemented by erasure [JLS, 4.6]. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime. Erasure is what allowed generic types to interoperate freely with legacy code that didn’t use generics (Item 26), ensuring a smooth transition to generics in Java 5.
数组和泛型之间的第二个主要区别:数组是具体化的 [JLS, 4.7]。这意味着数组在运行时知道并强制执行他们的元素类型。如前所述,如果试图将 String 元素放入一个 Long 类型的数组中,就会得到 ArrayStoreException。相比之下,泛型是通过擦除来实现的 [JLS, 4.6]。这意味着它们只在编译时执行类型约束,并在运行时丢弃(或擦除)元素类型信息。擦除允许泛型与不使用泛型的遗留代码自由交互操作([Item-26](/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md)),确保在 Java 5 中平稳地过渡。
Because of these fundamental differences, arrays and generics do not mix well. For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter. Therefore, none of these array creation expressions are legal: `new List<E>[]`, `new List<String>[]`, `new E[]`. All will result in generic array creation errors at compile time.
由于这些基本差异,数组和泛型不能很好地混合。例如,创建泛型、参数化类型或类型参数的数组是非法的。因此,这些数组创建表达式都不是合法的:`new List<E>[]`、`new List<String>[]`、`new E[]`。所有这些都会在编译时导致泛型数组创建错误。
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were legal, casts generated by the compiler in an otherwise correct program could fail at runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.
为什么创建泛型数组是非法的?因为这不是类型安全的。如果合法,编译器在其他正确的程序中生成的强制转换在运行时可能会失败,并导致 ClassCastException。这将违反泛型系统提供的基本保证。
To make this more concrete, consider the following code fragment:
为了更具体,请考虑以下代码片段:
```
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = List.of(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
```
Let’s pretend that line 1, which creates a generic array, is legal. Line 2 creates and initializes a `List<Integer>` containing a single element. Line 3 stores the `List<String>` array into an Object array variable, which is legal because arrays are covariant. Line 4 stores the `List<Integer>` into the sole element of the Object array, which succeeds because generics are implemented by erasure: the runtime type of a `List<Integer>` instance is simply List, and the runtime type of a `List<String>`[] instance is List[], so this assignment doesn’t generate an ArrayStoreException. Now we’re in trouble. We’ve stored a `List<Integer>` instance into an array that is declared to hold only `List<String>` instances. In line 5, we retrieve the sole element from the sole list in this array. The compiler automatically casts the retrieved element to String, but it’s an Integer, so we get a ClassCastException at runtime. In order to prevent this from happening, line 1 (which creates a generic array) must generate a compile-time error.
假设创建泛型数组的第 1 行是合法的。第 2 行创建并初始化一个包含单个元素的 `List<Integer>`。第 3 行将 `List<String>` 数组存储到 Object 类型的数组变量中,这是合法的,因为数组是协变的。第 4 行将 `List<Integer>` 存储到 Object 类型的数组的唯一元素中,这是成功的,因为泛型是由擦除实现的:`List<Integer>` 实例的运行时类型是 List,`List<String>`[] 实例的运行时类型是 List[],因此这个赋值不会生成 ArrayStoreException。现在我们有麻烦了。我们将一个 `List<Integer>` 实例存储到一个数组中,该数组声明只保存 `List<String>` 实例。在第 5 行,我们从这个数组的唯一列表中检索唯一元素。编译器自动将检索到的元素转换为 String 类型,但它是一个 Integer 类型的元素,因此我们在运行时得到一个 ClassCastException。为了防止这种情况发生,第 1 行(创建泛型数组)必须生成编译时错误。
Types such as E, `List<E>`, and `List<String>` are technically known as nonreifiable types [JLS, 4.7]. Intuitively speaking, a non-reifiable type is one whose runtime representation contains less information than its compile-time representation. Because of erasure, the only parameterized types that are reifiable are unbounded wildcard types such as `List<?>` and `Map<?,?>` (Item 26). It is legal, though rarely useful, to create arrays of unbounded wildcard types.
E、`List<E>` 和 `List<string>` 等类型在技术上称为不可具体化类型 [JLS, 4.7]。直观地说,非具体化类型的运行时表示包含的信息少于其编译时表示。由于擦除,唯一可具体化的参数化类型是无限制通配符类型,如 `List<?>` 和 `Map<?,?>`([Item-26](/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md))。创建无边界通配符类型数组是合法的,但不怎么有用。
The prohibition on generic array creation can be annoying. It means, for example, that it’s not generally possible for a generic collection to return an array of its element type (but see Item 33 for a partial solution). It also means that you get confusing warnings when using varargs methods (Item 53) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. The SafeVarargs annotation can be used to address this issue (Item 32).
禁止创建泛型数组可能很烦人。例如,这意味着泛型集合通常不可能返回其元素类型的数组(部分解决方案请参见 [Item-33](/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md))。这也意味着在使用 varargs 方法([Item-53](/Chapter-8/Chapter-8-Item-53-Use-varargs-judiciously.md))与泛型组合时,你会得到令人困惑的警告。这是因为每次调用 varargs 方法时,都会创建一个数组来保存 varargs 参数。如果该数组的元素类型不可具体化,则会得到警告。SafeVarargs 注解可以用来解决这个问题([Item-32](/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md))。
**译注:varargs 方法,指带有可变参数的方法。**
When you get a generic array creation error or an unchecked cast warning on a cast to an array type, the best solution is often to use the collection type `List<E>` in preference to the array type E[]. You might sacrifice some conciseness or performance, but in exchange you get better type safety and interoperability.
当你在转换为数组类型时遇到泛型数组创建错误或 unchecked 强制转换警告时,通常最好的解决方案是使用集合类型 `List<E>`,而不是数组类型 E[]。你可能会牺牲一些简洁性或性能,但作为交换,你可以获得更好的类型安全性和互操作性。
For example, suppose you want to write a Chooser class with a constructor that takes a collection, and a single method that returns an element of the collection chosen at random. Depending on what collection you pass to the constructor, you could use a chooser as a game die, a magic 8-ball, or a data source for a Monte Carlo simulation. Here’s a simplistic implementation without generics:
例如,假设你希望编写一个 Chooser 类,该类的构造函数接受一个集合,而单个方法返回随机选择的集合元素。根据传递给构造函数的集合,可以将选择器用作游戏骰子、魔术 8 球或蒙特卡洛模拟的数据源。下面是一个没有泛型的简单实现:
```
// Chooser - a class badly in need of generics!
public class Chooser {
private final Object[] choiceArray;
public Chooser(Collection choices) {
choiceArray = choices.toArray();
}
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray[rnd.nextInt(choiceArray.length)];
}
}
```
To use this class, you have to cast the choose method’s return value from Object to the desired type every time you use invoke the method, and the cast will fail at runtime if you get the type wrong. Taking the advice of Item 29 to heart, we attempt to modify Chooser to make it generic. Changes are shown in boldface:
要使用这个类,每次使用方法调用时,必须将 choose 方法的返回值从对象转换为所需的类型,如果类型错误,转换将在运行时失败。我们认真考虑了 [Item-29](/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) 的建议,试图对 Chooser 进行修改,使其具有通用性。变化以粗体显示:
```
// A first cut at making Chooser generic - won't compile
public class Chooser<T> {
private final T[] choiceArray;
public Chooser(Collection<T> choices) {
choiceArray = choices.toArray();
}
// choose method unchanged
}
```
If you try to compile this class, you’ll get this error message:
如果你尝试编译这个类,你将得到这样的错误消息:
```
Chooser.java:9: error: incompatible types: Object[] cannot be converted to T[]
choiceArray = choices.toArray();
^ where T is a type-variable:
T extends Object declared in class Chooser
```
No big deal, you say, I’ll cast the Object array to a T array:
没什么大不了的,你会说,我把对象数组转换成 T 数组:
```
choiceArray = (T[]) choices.toArray();
```
This gets rid of the error, but instead you get a warning:
这样就消除了错误,但你得到一个警告:
```
Chooser.java:9: warning: [unchecked] unchecked cast choiceArray = (T[]) choices.toArray();
^ required: T[], found: Object[]
where T is a type-variable:
T extends Object declared in class Chooser
```
The compiler is telling you that it can’t vouch for the safety of the cast at runtime because the program won’t know what type T represents—remember, element type information is erased from generics at runtime. Will the program work? Yes, but the compiler can’t prove it. You could prove it to yourself, put the proof in a comment and suppress the warning with an annotation, but you’re better off eliminating the cause of warning (Item 27).
编译器告诉你,它不能保证在运行时转换的安全性,因为程序不知道类型 T 代表什么。记住,元素类型信息在运行时从泛型中删除。这个计划会奏效吗?是的,但是编译器不能证明它。你可以向自己证明这一点,但是你最好将证据放在注释中,指出消除警告的原因([Item-27](/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md)),并使用注解隐藏警告。
To eliminate the unchecked cast warning, use a list instead of an array. Here is a version of the Chooser class that compiles without error or warning:
若要消除 unchecked 强制转换警告,请使用 list 而不是数组。下面是编译时没有错误或警告的 Chooser 类的一个版本:
```
// List-based Chooser - typesafe
public class Chooser<T> {
private final List<T> choiceList;
public Chooser(Collection<T> choices) {
choiceList = new ArrayList<>(choices);
}
public T choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList.get(rnd.nextInt(choiceList.size()));
}
}
```
This version is a tad more verbose, and perhaps a tad slower, but it’s worth it for the peace of mind that you won’t get a ClassCastException at runtime.
这个版本稍微有点冗长,可能稍微慢一些,但是为了让你安心,在运行时不会得到 ClassCastException 是值得的。
In summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequence, arrays provide runtime type safety but not compile-time type safety, and vice versa for generics. As a rule, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time errors or warnings, your first impulse should be to replace the arrays with lists.
总之,数组和泛型有非常不同的类型规则。数组是协变的、具体化的;泛型是不变的和可被擦除的。因此,数组提供了运行时类型安全,而不是编译时类型安全,对于泛型反之亦然。一般来说,数组和泛型不能很好地混合。如果你发现将它们混合在一起并得到编译时错误或警告,那么你的第一个反应该是将数组替换为 list。
---
**[Back to contents of the chapter(返回章节目录)](/Chapter-5/Chapter-5-Introduction.md)**
- **Previous Item(上一条目):[Item 27: Eliminate unchecked warnings(消除 unchecked 警告)](/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md)**
- **Next Item(下一条目):[Item 29: Favor generic types(优先使用泛型)](/Chapter-5/Chapter-5-Item-29-Favor-generic-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(考虑以序列化代理代替序列化实例)