为什么在这种情况下使用泛型会得到ClassCastException?
这是代码:一个简单的 ceeric 类并尝试为 分配一个整数aa[0]。
public class GenericTest<T> {
T [] aa = (T[]) new Object[2];
T bb;
public GenericTest(T x, T y) {
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
回答
事实上,异常信息是这样的:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
据说它不能将 an 转换Object[]为 an Integer[]。
的根本原因是初始化程序:
T [] aa = (T[]) new Object[2];
该类型转换是不安全的类型转换。事实上,编译器会告诉你有什么地方不对劲:
$ javac GenericTest.java
Note: GenericTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
无论如何......发生的事情是当你这样做时:
ll.aa[0] = 6;
JVM 正试图将 强制ll.aa转换为Integer[]... 因为这是静态类型所说的应该是。但它不是一个Integer[]. 它是一个Object[]. 因为Object[]与Integer[]给你一个类转换异常的赋值不兼容。
(为什么要进行隐藏类型转换?这就是 JVM 在面对可能的不安全类型转换等情况时确保运行时类型安全的方式!)
如何解决?
避免使用T[]. 使用List<T>来代替。
不幸的是,如果您必须使用T[],则没有简单的修复方法。基本上很难创建泛型类型参数的数组。您最终不得不Class将参数的实际类的对象作为额外参数传递。像这样的东西:
import java.lang.reflect.Array;
public class GenericTest<T> {
T [] aa;
T bb;
public GenericTest(Class<T> cls, T x, T y) {sy
aa = (T[]) Array.newInstance(cls, 2);
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(Integer.class, 1, 2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
仍然存在关于不安全类型转换的警告……但在这种情况下,可以安全地取消警告。
对于 Java 8 以后,还有另一种解决方案,它涉及传递对数组构造函数的引用Integer[];见安迪特纳的回答。这比使用反射和调用更干净Array.newInstance,但您仍然需要向构造函数传递一个额外的参数。