一尘不染

泛型,数组和ClassCastException

java

我认为这里肯定有些微妙的事情我不知道。考虑以下:

public class Foo<T> {
  private T[] a = (T[]) new Object[5];

  public Foo() {
    // Add some elements to a
  }

  public T[] getA() {
    return a;
  }
}

假设您的main方法包含以下内容:

Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();

您将收到一条无法转换为CastClassException的消息。java.lang.Object``java.lang.Double

谁能告诉我为什么?我的理解ClassCastException是,当您尝试将对象强制转换为无法强制转换的类型时,会抛出该错误。也就是说,对于不是实例的子类(引用文档)。例如:

Object o = new Double(3.);
Double d = (Double) o; // Working cast
String s = (String) o; // ClassCastException

看来我可以做到。如果a只是一个T而不是一个数组T[],我们可以a毫无问题地获取并强制转换它。为什么数组会破坏这一点?

谢谢。


阅读 256

收藏
2020-09-08

共1个答案

一尘不染

Foo<Double> f = new Foo<Double>();

使用泛型类Foo的此版本时,然后对于成员变量a,编译器实质上采用以下行:

private T[] a = (T[]) new Object[5];

和更换TDouble得到这个:

private Double[] a = (Double[]) new Object[5];

您不能从Object转换为Double,因此不能转换为ClassCastException。

更新和说明: 实际上,在运行一些测试代码之后,ClassCastException比这更微妙。例如,这个main方法可以正常工作,没有任何异常:

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    System.out.println(f.getA());
}

当您尝试分配f.getA()给以下类型的引用时,会发生此问题Double[]

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    Double[] a2 = f.getA(); // throws ClassCastException
    System.out.println(a2);
}

这是因为有关成员变量的类型信息a在运行时会被删除。泛型仅在 编译时 提供类型安全性(我在最初的文章中以某种方式忽略了这一点)。所以问题不在于

private T[] a = (T[]) new Object[5];

因为在运行时这段代码确实

private Object[] a = new Object[5];

当方法的结果(getA()在运行时实际上返回Object[])被分配给类型的引用时,就会发生问题,Double[]因为无法将对象强制转换为Double,因此该语句引发ClassCastException。

更新2 :回答您的最后一个问题“为什么数组会破坏它?”
答案是因为语言规范不支持通用数组创建。有关更多信息,请参见此论坛帖子
-为了向后兼容,在运行时对T的类型一无所知。

2020-09-08