泛型,数组和ClassCastException

nto*_*end 15 java arrays generics classcastexception

我认为这里肯定会有一些我不知道的微妙内容.考虑以下:

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

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

  public T[] getA() {
    return a;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();
Run Code Online (Sandbox Code Playgroud)

你将获得一个无法投射CastClassException的消息.java.lang.Objectjava.lang.Double

谁能告诉我为什么?我的理解ClassCastException是,当您尝试将对象转换为无法转换的类型时,它会被抛出.也就是说,它不是一个实例的子类(引用文档).例如:

Object o = new Double(3.);
Double d = (Double) o; // Working cast
String s = (String) o; // ClassCastException
Run Code Online (Sandbox Code Playgroud)

似乎我可以做到这一点.如果a只是一个T数组T[],而不是一个数组,我们可以a毫无问题地得到它.为什么数组打破了这个?

谢谢.

mat*_*t b 20

Foo<Double> f = new Foo<Double>();
Run Code Online (Sandbox Code Playgroud)

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

private T[] a = (T[]) new Object[5];
Run Code Online (Sandbox Code Playgroud)

和更换TDouble得到这个:

private Double[] a = (Double[]) new Object[5];
Run Code Online (Sandbox Code Playgroud)

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

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

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

当您尝试分配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);
}
Run Code Online (Sandbox Code Playgroud)

这是因为有关成员变量的类型信息a在运行时被删除.泛型只在编译时提供类型安全性(我在最初的帖子中忽略了这一点).所以问题不是

private T[] a = (T[]) new Object[5];
Run Code Online (Sandbox Code Playgroud)

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

private Object[] a = new Object[5];
Run Code Online (Sandbox Code Playgroud)

getA()运行时实际返回的方法的结果Object[]被分配给类型的引用时,会出现问题Double[]- 此语句抛出ClassCastException,因为Object无法强制转换为Double.

更新2:回答你的最后一个问题"为什么数组会打破这个?" 答案是因为语言规范不支持通用数组创建.有关更多内容,请参阅此论坛帖子 - 为了向后兼容,在运行时对T的类型一无所知.


Mis*_*ble 5

@ mattb的解释可能会有一些小错误.

错误不是

java.lang.Object无法强制转换为java.lang.Double.

它是:

[Ljava.lang.Object; 无法转换为[Ljava.lang.Double

[L表示一个数组.也就是说,错误是一个阵列的对象不能被转换为双阵列.这与以下情况相同:

Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;
Run Code Online (Sandbox Code Playgroud)

这显然是不允许的.

对于创建类型安全数组的问题,另一种方法是在init中使用类对象,并使用Array.newInstance:

import java.lang.reflect.Array;

class Foo<T> {
  private T[] a ;

  public Foo(Class<T> tclass) {
    a = (T[]) Array.newInstance(tclass, 5);
  }

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

  public static <T> Foo<T> create(Class<T> tclass) {
    return new Foo<T>(tclass);
  }
}

class Array1
{
  public static final void main(final String[] args) {
    Foo<Double> f = Foo.create(Double.class);
    Double[] d = f.getA();
  }


}
Run Code Online (Sandbox Code Playgroud)