generics,数组和ClassCastException

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

public class Foo { private T[] a = (T[]) new Object[5]; public Foo() { // Add some elements to a } public T[] getA() { return a; } } 

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

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

您将获得带有消息java.lang.ObjectCastClassException ,无法将其CastClassExceptionjava.lang.Double

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

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

似乎我能做到这一点。 如果a只是一个T而不是数组T[] ,我们可以得到a并且没有问题地抛出它。 为什么数组打破了这个?

谢谢。

 Foo f = new Foo(); 

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

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

并用Double替换T得到这个:

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

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

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

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

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

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

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

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

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

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

当在运行时实际返回Object[]的方法getA()的结果被赋值给Double[]类型的引用时,会出现问题 – 此语句抛出ClassCastException,因为Object不能转换为Double。

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

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

错误不是

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

它是:

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

[L表示一个数组。 也就是说,错误是无法将对象数组强制转换为Double数组。 这与以下情况相同:

 Object[] oarr = new Object[10]; Double[] darr = (Double[]) oarr; 

这显然是不允许的。

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

 import java.lang.reflect.Array; class Foo { private T[] a ; public Foo(Class tclass) { a = (T[]) Array.newInstance(tclass, 5); } public T[] getA() { return a; } public static  Foo create(Class tclass) { return new Foo(tclass); } } class Array1 { public static final void main(final String[] args) { Foo f = Foo.create(Double.class); Double[] d = f.getA(); } } 

@matt b:谢谢你的回答! 很有帮助。

我找到了感兴趣的解决方法:给getA方法一个初始化的数组来填充。 这样,类型信息可用。

 public class Foo { private T[] a = (T[]) new Object[5]; public Foo() { // Add some elements to a } public T[] getA(T[] holdA) { // Check whether holdA is null and handle it...then: holdA = (T[]) Array.newInstance(holdA.getClass().getComponentType(), a.length); System.arraycopy(a, 0, holdA, 0, a.length); return holdA; } } 

然后为您的主要方法:

 public static void main(String[] args) { Foo f = new Foo(); Double[] a2 = new Double[1]; a2 = f.getA(a2); }