不要使用原始类型

泛型类或泛型接口

一个类或接口,它的声明有一个或多个类型参数(type parameters ),被称之为泛型类泛型接口

例如,List 接口具有单个类型参数 E,表示其元素类型。 接口的全名是 List<E>(读作「E」的列表),但是人们经常称它为 List

泛型类和接口统称为泛型类型(generic types)。

参数化类型

每个泛型定义了一组参数化类型(parameterized types),它们由类或接口名称组成,后跟一个与泛型类型的形式类型参数相对应的实际类型参数的尖括号「<>」列表。

例如,List<String>(读作「字符串列表」)是一个参数化类型,表示其元素类型为 String 的列表。 (String 是与形式类型参数 E 相对应的实际类型参数)。

原始类型

每个泛型定义了一个原始类型(raw type),它是没有任何类型参数的泛型类型的名称。

例如,对应于 List<E> 的原始类型是 List

原始类型的行为就像所有的泛型类型信息都从类型声明中被清除一样。 它们的存在主要是为了与没有泛型之前的代码相兼容。

不要使用原始类型

使用原始类型(没有类型参数的泛型)是合法的,但是你不应该这样做。

如果你使用原始类型,则会丧失泛型的所有安全性和表达上的优势。 鉴于你不应该使用它们,为什么语言设计者首先允许原始类型呢? 答案是为了兼容性。

泛型被添加时,Java 即将进入第二个十年,并且有大量的代码没有使用泛型。 所有这些代码都是合法的,并且与使用泛型的新代码进行交互操作被认为是至关重要的。 将参数化类型的实例传递给为原始类型设计的方法必须是合法的,反之亦然。 这个需求,被称为迁移兼容性,驱使决策支持原始类型,并使用擦除来实现泛型(详见第 28 条)。

List vs List<Object>

虽然不应使用 List 之类的原始类型,但可以使用参数化类型来允许插入任意对象(如 List<Object>)。

原始类型 List 和参数化类型 List<Object> 之间有什么区别? 松散地说,前者已经选择了泛型类型系统,而后者明确地告诉编译器,它能够保存任何类型的对象。

虽然可以将 List<String> 传递给 List 类型的参数,但不能将其传递给 List<Object> 类型的参数。

泛型有子类型的规则,List<String> 是原始类型 List 的子类型,但不是参数化类型 List<Object> 的子类型(条目 28)。

因此,如果使用诸如 List 之类的原始类型,则会丢失类型安全性,但是如果使用参数化类型(例如 List<Object>)则不会。

总之,使用原始类型可能导致运行时异常,所以不要使用它们。 它们仅用于与泛型引入之前的传统代码的兼容性和互操作性。 作为一个快速回顾,Set<Object> 是一个参数化类型,表示一个可以包含任何类型对象的集合,Set<?> 是一个通配符类型,表示一个只能包含某些未知类型对象的集合,Set 是一个原始类型,它不在泛型类型系统之列。 前两个类型是安全的,最后一个不是。

示例

总结

使用原始类型可能导致运行时异常,所以不要使用它们。

它们仅用于与泛型引入之前的传统代码的兼容性和互操作性。

前两个类型是安全的,最后一个不是。

术语

为了快速参考,下表中总结了本条目(以及本章稍后介绍的一些)中介绍的术语:

术语 中文含义 举例 所在条目
Parameterized type 参数化类型 List<String> 条目 26
Actual type parameter 实际类型参数 String 条目 26
Generic type 泛型类型 List<E> 条目 26
Formal type parameter 形式类型参数 E 条目 26
Unbounded wildcard type 无限制通配符类型 List<?> 条目 26
Raw type 原始类型 List 条目 26
Bounded type parameter 限制类型参数 <E extends Number> 条目 29
Recursive type bound 递归类型限制 <T extends Comparable<T>> 条目 30
Bounded wildcard type 限制通配符类型 List<? extends Number> 条目 31
Generic method 泛型方法 static <E> List<E> asList(E[] a) 条目 30
Type token 类型令牌 String.class 条目 33