## 不要使用原始类型 #### 泛型类或泛型接口 一个类或接口,它的声明有一个或多个类型参数(type parameters ),被称之为**泛型类**或**泛型接口**。 例如,`List` 接口具有单个类型参数 E,表示其元素类型。 接口的全名是 `List`(读作「E」的列表),但是人们经常称它为 `List`。 泛型类和接口统称为泛型类型(generic types)。 #### 参数化类型 每个泛型定义了一组**参数化类型**(parameterized types),它们由类或接口名称组成,后跟一个与泛型类型的形式类型参数相对应的实际类型参数的尖括号「<>」列表。 例如,`List`(读作「字符串列表」)是一个参数化类型,表示其元素类型为 String 的列表。 (`String` 是与形式类型参数 `E` 相对应的实际类型参数)。 #### 原始类型 每个泛型定义了一个原始类型(raw type),它是没有任何类型参数的泛型类型的名称。 例如,对应于 `List` 的原始类型是 `List`。 原始类型的行为就像所有的泛型类型信息都从类型声明中被清除一样。 它们的存在主要是为了与没有泛型之前的代码相兼容。 #### 不要使用原始类型 使用原始类型(没有类型参数的泛型)是合法的,但是你不应该这样做。 **如果你使用原始类型,则会丧失泛型的所有安全性和表达上的优势。** 鉴于你不应该使用它们,为什么语言设计者首先允许原始类型呢? 答案是为了兼容性。 泛型被添加时,Java 即将进入第二个十年,并且有大量的代码没有使用泛型。 所有这些代码都是合法的,并且与使用泛型的新代码进行交互操作被认为是至关重要的。 将参数化类型的实例传递给为原始类型设计的方法必须是合法的,反之亦然。 这个需求,被称为迁移兼容性,驱使决策支持原始类型,并使用擦除来实现泛型(详见第 28 条)。 #### `List` vs `List` 虽然不应使用 `List` 之类的原始类型,但可以使用参数化类型来允许插入任意对象(如 `List`)。 原始类型 List 和参数化类型 `List` 之间有什么区别? 松散地说,前者已经选择了泛型类型系统,而后者明确地告诉编译器,它能够保存任何类型的对象。 虽然可以将 `List` 传递给 `List` 类型的参数,但不能将其传递给 `List` 类型的参数。 泛型有子类型的规则,`List` 是原始类型 `List` 的子类型,但不是参数化类型 `List` 的子类型(条目 28)。 因此,如果使用诸如 `List` 之类的原始类型,则会丢失类型安全性,但是如果使用参数化类型(例如 `List`)则不会。 总之,使用原始类型可能导致运行时异常,所以不要使用它们。 它们仅用于与泛型引入之前的传统代码的兼容性和互操作性。 作为一个快速回顾,`Set` 是一个参数化类型,表示一个可以包含任何类型对象的集合,`Set` 是一个通配符类型,表示一个只能包含某些未知类型对象的集合,`Set` 是一个原始类型,它不在泛型类型系统之列。 前两个类型是安全的,最后一个不是。 #### 示例 - [Item26Example01.java](Generics/src/main/java/com/jueee/item26/Item26Example01.java):【×】使用诸如 List 之类的原始类型,则会丢失类型安全性,会收到警告。 - [Item26Example02.java](Generics/src/main/java/com/jueee/item26/Item26Example02.java):【×】用参数化类型 `List` 替换原始类型 `List`,会发出错误消息。 - [Item26Example03.java](Generics/src/main/java/com/jueee/item26/Item26Example03.java):【√】使用正确的参数化类型 `List`。 - [Item26Example04.java](Generics/src/main/java/com/jueee/item26/Item26Example04.java):【×】使用**原始类型**来处理元素类型未知且无关紧要的集合。 - [Item26Example05.java](Generics/src/main/java/com/jueee/item26/Item26Example05.java):【√】使用**无限制通配符类型**来处理元素类型未知且无关紧要的集合。 - [Item26Example06.java](Generics/src/main/java/com/jueee/item26/Item26Example06.java):使用泛型类型的 instanceof 运算符的首选方法。 #### 总结 使用原始类型可能导致运行时异常,所以不要使用它们。 它们仅用于与泛型引入之前的传统代码的兼容性和互操作性。 - `Set` 是一个参数化类型,表示一个可以包含任何类型对象的集合。 - `Set` 是一个通配符类型,表示一个只能包含某些未知类型对象的集合。 - `Set` 是一个原始类型,它不在泛型类型系统之列。 前两个类型是安全的,最后一个不是。 #### 术语 为了快速参考,下表中总结了本条目(以及本章稍后介绍的一些)中介绍的术语: | 术语 | 中文含义 | 举例 | 所在条目 | | ----------------------- | ---------------- | ---------------------------------- | -------- | | Parameterized type | 参数化类型 | `List` | 条目 26 | | Actual type parameter | 实际类型参数 | `String` | 条目 26 | | Generic type | 泛型类型 | `List` | 条目 26 | | Formal type parameter | 形式类型参数 | `E` | 条目 26 | | Unbounded wildcard type | 无限制通配符类型 | `List` | 条目 26 | | Raw type | 原始类型 | `List` | 条目 26 | | Bounded type parameter | 限制类型参数 | `` | 条目 29 | | Recursive type bound | 递归类型限制 | `>` | 条目 30 | | Bounded wildcard type | 限制通配符类型 | `List` | 条目 31 | | Generic method | 泛型方法 | `static List asList(E[] a)` | 条目 30 | | Type token | 类型令牌 | `String.class` | 条目 33 |