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.Object
的CastClassException
,无法将其CastClassException
为java.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); }