Java 泛型、数组和ClassCastException

Java 泛型、数组和ClassCastException,java,arrays,generics,classcastexception,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; } } Foo<Double> f = new Foo<Double>(); Double[] d

我想这里一定发生了一些我不知道的微妙的事情。考虑以下事项:

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

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

  public T[] getA() {
    return a;
  }
}
Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();
Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;
看来我能做到。如果
a
只是一个
T
而不是一个数组
T[]
,那么我们可以获得
a
并毫无问题地将其强制转换。为什么阵列会破坏这一点

谢谢

Foo<Double> f = new Foo<Double>();
并将
T
替换为
Double
,以获得:

private Double[] a = (Double[]) new Object[5];
不能从Object强制转换为Double,因此存在ClassCastException

更新和澄清:实际上,在运行一些测试代码之后,ClassCastException比这更微妙。例如,此主要方法将毫无例外地正常工作:

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    System.out.println(f.getA());
}
这是因为有关成员变量
a
的类型信息在运行时被擦除。泛型只在编译时提供类型安全性(我在最初的文章中忽略了这一点)。所以问题不在这里

private T[] a = (T[]) new Object[5];
因为在运行时,这段代码实际上是

private Object[] a = new Object[5];
当方法
getA()
的结果(在运行时实际返回一个
对象[]
)被分配给类型为
Double[]
的引用时,就会出现问题-此语句抛出ClassCastException,因为对象不能转换为Double


更新2:要回答您的最后一个问题“为什么数组会破坏它?”答案是因为语言规范不支持通用数组创建。-为了向后兼容,运行时对T的类型一无所知。

@matt b:谢谢你的回答!非常有用

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

public class Foo<T> {
  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;
  }
}
公共类Foo{
私有T[]a=(T[])新对象[5];
公共食物({
//将一些元素添加到
}
公共T[]getA(T[]holdA){
//检查holdA是否为null并处理它…然后:
holdA=(T[])Array.newInstance(holdA.getClass().getComponentType(),a.length);
数组复制(a,0,holdA,0,a.length);
返回holdA;
}
}
那么对于您的主要方法:

public static void main(String[] args) {
  Foo<Double> f = new Foo<Double>();
  Double[] a2 = new Double[1];
  a2 = f.getA(a2);
}
publicstaticvoidmain(字符串[]args){
Foo f=新的Foo();
Double[]a2=新的Double[1];
a2=f.getA(a2);
}

在@mattb的解释中可能有一些小错误

这个错误是不正确的

无法将java.lang.Object转换为java.lang.Double

它是:

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

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

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

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

  public T[] getA() {
    return a;
  }
}
Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();
Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;
这显然是不允许的

对于创建类型安全数组的问题,另一种选择是在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();
  }


}
import java.lang.reflect.Array;
福班{
私人T[]a;
公共食品(tclass类){
a=(T[])Array.newInstance(tclass,5);
}
公共T[]getA(){
返回a;
}
公共静态Foo创建(类tclass){
返回新的Foo(tclass);
}
}
类数组1
{
公共静态最终void main(最终字符串[]args){
Foo f=Foo.create(Double.class);
Double[]d=f.getA();
}
}

你是对的,在阅读了我在回答中发布的链接后,我想:)对于所有想阅读它的人来说:?上面的链接有一些很好的信息-可能有助于原始问题询问者了解更多关于类型擦除以及在运行时与编译时-1中已知的内容,因为解释不清楚且混乱。Pandya的一个是Java编译器通过“如果类型参数是无界的,则用它们的边界或对象替换泛型类型中的所有类型参数”(ref:),来擦除泛型,所以您说“用Double替换T”实际上,这是因为你不能创建通用数组。你是否考虑过使用数组来代替?是否有一种情况,你可以从A类型转换成B而不是A到B []?你不能从A [B ]转换成B[]。考虑两个类水果和苹果(延伸水果)。。所以你可以从苹果到水果。但你不能从苹果[]到水果[]。因为你可以将/any/Fruit(比如橙色)添加到水果[],但只能将苹果添加到苹果[]。哦,等等,我不知道我在说什么。对于两个类C1