列表优于数组
数组在两个重要方面与泛型不同。 首先,数组是协变的(covariant)。 这个吓人的单词意味着如果 Sub 是 Super 的子类型,则数组类型 Sub[]
是数组类型 Super[]
的子类型。 相比之下,泛型是不变的(invariant):对于任何两种不同的类型 Type1
和 Type2
,List<Type1>
既不是 List<Type2>
的子类型也不是父类型。 你可能认为这意味着泛型是不足的,但可以说是数组缺陷。
示例代码:Item28Example01.java:数组和泛型对比。
由于这些基本差异,数组和泛型不能很好地在一起混合使用。 例如,创建泛型类型的数组,参数化类型的数组,以及类型参数的数组都是非法的。 因此,这些数组创建表达式都不合法:new List<E>[]
,new List<String>[]
,new E[]
。 所有将在编译时导致泛型数组创建错误。
类型 E
,List<E>
和 List<String>
等在技术上被称为不可具体化的类型(nonreifiable types)。 直观地说,不可具体化的类型是其运行时表示包含的信息少于其编译时表示的类型。 由于擦除,可唯一确定的参数化类型是无限定通配符类型,如 List<?>
和 Map<?, ?>
(详见第 26 条)。 尽管很少有用,创建无限定通配符类型的数组是合法的。
当你在强制转换为数组类型时,得到泛型数组创建错误,或是未经检查的强制转换警告时,最佳解决方案通常是使用集合类型 List<E>
而不是数组类型 E[]
。 这样可能会牺牲一些简洁性或性能,但作为交换,你会获得更好的类型安全性和互操作性。
假设你想用带有集合的构造方法来编写一个 Chooser
类,并且有个方法返回随机选择的集合的一个元素:
-
Item28Example02.java:没有泛型的简单实现。
要使用这个类,每次调用方法时,都必须将
Object
的choose
方法的返回值转换为所需的类型,如果类型错误,则转换在运行时失败。 -
Item28Example03.java:修改
Chooser
类,使其成为泛型的。编译器告诉你在运行时不能保证强制转换的安全性,因为程序不会知道
T
代表什么类型。 -
Item28Example04.java:要消除未经检查的强制转换警告,请使用列表而不是数组。
总之,数组和泛型具有非常不同的类型规则。 数组是协变和具体化的; 泛型是不变的,类型擦除的。 因此,数组提供运行时类型的安全性,但不提供编译时类型的安全性,反之亦然。
一般来说,数组和泛型不能很好地混合工作。 如果你发现把它们混合在一起,得到编译时错误或者警告,你的第一个冲动应该是用列表来替换数组。