我对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];
如果没有出现故障,为什么不这样做呢?
答案 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);