为什么下面的代码可能不是类型安全的(编译器会生成警告)?
class ArrayTypeErasure<T> {
private T[] elements;
public void setElements(List<T> elements) {
this.elements = (T[]) elements.toArray();
}
}我正在尝试想出代码会失败的任何情况,但到目前为止,只有我失败了。
发布于 2019-03-11 07:41:37
首先,请注意,数组在运行时知道它们的组件类型,但泛型类的实例在运行时不知道泛型类型参数。在没有进一步信息的情况下,List<T>无法在运行时创建运行时类型为T[]的数组对象,因为它在运行时不知道T是什么。接受一个数组参数的List#toArray()方法使用传入的数组实例的运行时类型在运行时构造相同组件类型的数组。但是不带参数的List#toArray()总是创建一个运行时类型为Object[]的数组。因此,elements.toArray()的计算结果是一个运行时类型始终为Object[]的数组实例。
Object[]不是T[]的子类型(当T不是Object时),因此将此数组赋给编译时类型为this.elements的T[]是错误的。但是,它不会立即导致任何异常,因为T的擦除是Object,所以T[]的擦除是Object[],将Object[]赋给Object[]就可以了。只要您确保不将包含在this.elements中的对象作为T[]公开给此对象的外部,它就不会引起任何问题。但是,如果将this.elements中包含的对象作为类型T[]公开给类外部(例如,将this.elements作为类型T[]返回的方法,或者如果将this.elements设置为公共或受保护的字段),则类外部的调用方可能希望T是特定类型,这可能会导致类强制转换异常。
例如,如果您有一个以T[]类型返回this.elements的方法
public T[] getElements() {
return this.elements;
}然后你有一个持有ArrayTypeErasure<String>的调用者,它在上面调用.getElements(),它将会得到一个String[]。由于对象的运行时类型为Object[],因此当它尝试将结果赋给String[]时,将导致类类型转换异常
ArrayTypeErasure<String> foo = ...
String[] bar = foo.getElements();发布于 2019-03-06 22:13:09
很简单,因为List#toArray()返回一个Object[],所以这个方法不能保证它会返回T[]
现在,在实践中,这是可以的,因为您总是知道它将返回所需的类型
您可以使用@SuppressWarnings("unchecked")来避免出现此警告
发布于 2019-03-06 22:14:03
由于类型擦除,您可以将List<X>%s转换为List<Y>%s。
ArrayTypeErasure<String> er = new ArrayTypeErasure<>();
ArrayTypeErasure erased = er;
List intList = new List<Integer>(); // compile warnings, but
intList.add(1);
erased.setElements(intList);https://stackoverflow.com/questions/55025051
复制相似问题