为什么转换Object []数组是错误的?

时间:2015-09-15 13:47:51

标签: java arrays object generics

我对Java中的泛型编程非常陌生。

我不明白为什么无法创建泛型类型的数组。

T[] a = new T[size]; //why does this give an error?

如果泛型类型意味着通用占位符T在运行时将被类名替换,那么是什么阻止我们创建具有泛型引用的数组?

经过一番搜索,我发现了一个解决方法

T[] a = (T[])new Object[size];   //I don't get how this works?

虽然我找到了解决方案,但我仍然无法理解阻止创建通用数组的原因。 假设我创建了一个返回Object数组的函数。

public Object[] foo(){
        return new Object[12];
}

然后拨打电话

String[] a = (String[])foo();

给出ClassCastException。但为什么? 它看起来与我将Object数组转换为T数组的第一行代码相似吗?

 T[] a = (T[])new Object[size];

如果没有出现故障,为什么不这样做呢?

3 个答案:

答案 0 :(得分:3)

处理数组时需要注意几点。

首先,数组被认为是协变;也就是说,类型化数组将保持其继承链。因此,Integer[]与[{1}}相同,Object[]Integer

这就是你最后一个例子失败的原因。您想将Object投射到Object[]String[]

foo

String[] a = (String[])foo(); 永远不会是Object[],因为String[]不是Object(但相反的情况总是如此)。

其次,数组和泛型不能很好地混合。泛型被认为是不变的;也就是说,他们不会维持他们的继承链。 String 被认为与List<Integer>相同。

至于您的特定示例失败的原因,这是由于编译时类型擦除。数组需要在编译时知道它们的具体类型,如果没有这些信息,它们就无法实例化。由于泛型不存储该信息,因此您无法像实例化非泛型数组那样实例化通用数组。

也就是说,必须使用强制转换形式:

List<Object>

You can read a bit more about generic arrays in this answer,因为它涵盖了处理它们时需要了解的大部分要点。

答案 1 :(得分:3)

部分重点是反过来看待它。你做不到(String[]) new Object[10];因为Object数组不是一个String数组。因为

String[] array = new String[10];
array[0] = "foo";
String foo = array[0];

很好,但是

Object[] objectArray = new Object[10];
objectArray[0] = 10;
String[] stringArray = (String[]) objectArray;
String foo = stringArray[0];

...正在尝试将Integer分配给String,这不应该首先被允许。因此,当您将Object[]转换为String[]时,此代码会失败。该代码在某处抛出ClassCastException

即使在首次发明仿制药之前,Java也是如此。接受所有第一个然后转到泛型。

现在,Java泛型实现的方式意味着在编译代码时,T会被静默重写为Object。因此,T[] array = (T[]) new Object[10]被默默地允许,因为它实际上被重写为Object[] array = new Object[10]。但是一旦你把它拿出来,事情就出错了。例如,

private static <T> T[] newArray() {
   return (T[]) new Object[10];
}

如果您致电String[] array = newArray(),您将在通话网站上获得ClassCastException,而不是newArray()。这就是为什么Java会在(T[]) new Object[10]向您发出警告,并且该警告可能会在以后导致真正的ClassCastException

一般来说,不要混合数组和泛型。解决所有这些问题的方法是正确使用List

答案 2 :(得分:0)

数组在运行时知道它们的类型。 String[]知道它是String s的数组。

与此相反,泛型类型参数在运行时被擦除,因此List<String>在运行时只是List

由于类型参数T在运行时new T[10]不可用(它没有编译),因此无法创建真正的T[]

并非如此
T[] a = (T[])new Object[size];

不能抛出异常。它可以。 Louis Wasserman的例子表明它可以在调用站点引起异常,但该行也可以直接抛出异常。例如

public static void main(String[] args) {
    foo();
}

static <T extends Number> void foo() {
    T[] array = (T[]) new Object[42];
}

这里,T的下限是Number,所以在运行时,会尝试将Object[]强制转换为Number[],这会抛出ClassCastException }}

如果您拥有T[]对象Class<T>,则可以创建clazz,例如使用

Array.newInstance(clazz, length);