为什么在这种情况下使用泛型会得到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,但您仍然需要向构造函数传递一个额外的参数。


以上是为什么在这种情况下使用泛型会得到ClassCastException?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>