16

我认为这里一定有一些我不知道的微妙之处。考虑以下:

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.Objectjava.lang.Double

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

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

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

谢谢。

4

3 回答 3

21
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()给 type 的引用时会出现问题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()在运行时实际返回 an的方法的结果Object[]被分配给类型的引用时,就会出现问题Double[]- 此语句抛出 ClassCastException,因为 Object 不能强制转换为 Double。

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

于 2008-12-16T18:46:18.417 回答
5

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

错误不是

java.lang.Object 不能强制转换为 java.lang.Double。

这是:

[Ljava.lang.Object; 不能转换为 [Ljava.lang.Double

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

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();
  }


}
于 2008-12-17T04:21:45.993 回答
3

@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;
  }
}

然后对于您的主要方法:

public static void main(String[] args) {
  Foo<Double> f = new Foo<Double>();
  Double[] a2 = new Double[1];
  a2 = f.getA(a2);
}
于 2008-12-16T20:04:25.060 回答