Effective Java 第三版——28. 列表优于数组 (2)

当你在强制转换为数组类型时,得到泛型数组创建错误,或是未经检查的强制转换警告时,最佳解决方案通常是使用集合类型List <E>而不是数组类型E []。 这样可能会牺牲一些简洁性或性能,但作为交换,你会获得更好的类型安全性和互操作性。

例如,假设你想用带有集合的构造方法来编写一个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)]; } }

要使用这个类,每次调用方法时,都必须将Object的choose方法的返回值转换为所需的类型,如果类型错误,则转换在运行时失败。 我们先根据条目 29的建议,试图修改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 }

如果你尝试编译这个类,会得到这个错误信息:

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

没什么大不了的,将Object数组转换为T数组:

choiceArray = (T[]) choices.toArray();

这没有了错误,而是得到一个警告:

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

编译器告诉你在运行时不能保证强制转换的安全性,因为程序不会知道T代表什么类型——记住,元素类型信息在运行时会被泛型删除。 该程序可以正常工作吗? 是的,但编译器不能证明这一点。 你可以证明这一点,在注释中提出证据,并用注解来抑制警告,但最好是消除警告的原因(条目 27)。

要消除未经检查的强制转换警告,请使用列表而不是数组。 下面是另一个版本的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())); } }

这个版本有些冗长,也许运行比较慢,但是值得一提的是,在运行时不会得到ClassCastException异常。

总之,数组和泛型具有非常不同的类型规则。 数组是协变和具体化的; 泛型是不变的,类型擦除的。 因此,数组提供运行时类型的安全性,但不提供编译时类型的安全性,反之亦然。 一般来说,数组和泛型不能很好地混合工作。 如果你发现把它们混合在一起,得到编译时错误或者警告,你的第一个冲动应该是用列表来替换数组。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyywgg.html