尝试在泛型中使用原始类型时出现奇怪的编译时行为
-
20-09-2019 - |
题
import java.lang.reflect.Array;
public class PrimitiveArrayGeneric {
static <T> T[] genericArrayNewInstance(Class<T> componentType) {
return (T[]) Array.newInstance(componentType, 0);
}
public static void main(String args[]) {
int[] intArray;
Integer[] integerArray;
intArray = (int[]) Array.newInstance(int.class, 0);
// Okay!
integerArray = genericArrayNewInstance(Integer.class);
// Okay!
intArray = genericArrayNewInstance(int.class);
// Compile time error:
// cannot convert from Integer[] to int[]
integerArray = genericArrayNewInstance(int.class);
// Run time error:
// ClassCastException: [I cannot be cast to [Ljava.lang.Object;
}
}
我试图完全理解泛型在 Java 中是如何工作的。在上面代码片段的第三个作业中,事情对我来说有点奇怪:编译器抱怨说 Integer[]
不能转换为 int[]
. 。当然,这个说法 100% 正确,但我想知道 为什么 编译器正在提出此投诉。
如果您注释该行,并遵循编译器的“建议”,如第四个作业中所示, 编译器实际上很满意! 现在 代码编译得很好!当然,这很疯狂,因为就像运行时行为表明的那样, int[]
不能转换为 Object[]
(这是什么 T[]
在运行时被类型擦除)。
所以我的问题是:为什么编译器“建议”我分配给 Integer[]
而不是第三个作业?编译器如何得出这个(错误的!)结论?
到目前为止,这两个答案存在很多混乱,因此我创建了另一个令人困惑的示例来说明此处的根本问题:
public class PrimitiveClassGeneric {
static <T extends Number> T test(Class<T> c) {
System.out.println(c.getName() + " extends " + c.getSuperclass());
return (T) null;
}
public static void main(String args[]) {
test(Integer.class);
// "java.lang.Integer extends class java.lang.Number"
test(int.class);
// "int extends null"
}
}
我是唯一一个认为编译器让上述代码编译绝对疯狂的人吗?
打印出来也不是没有道理 c.getSuperclass().getName()
例如,在上面的代码中,因为我指定了 T extends Number
. 。当然现在 getName()
会扔 NullPointerException
什么时候 c == int.class
, , 自从 c.getSuperclass() == null
.
对我来说,这是首先拒绝编译代码的一个很好的理由。
也许最终的疯狂是:
int.class.cast(null);
该代码编译 和 运行良好。
解决方案
在型int.class
的是Class<Integer>
,所以genericArrayNewInstance()
将被推断为返回一个Integer[]
。但是函数实际上创建了一个int[]
,所以返回时,它会有一个类转换异常。基本上,浇铸到T[]
内部的功能在这种情况下合法的,因为int[]
不是T[]
(基元不能在类型变量可以使用)。你不能笼统处理基本数组类型;所以你要么必须有你的方法只是返回类型Object
,或者你必须做出引用类型和原始类型不同的方法。
其他提示
几点:
- 原语是 自动装箱 需要时到其对象对应物(包装器)
- 原始数组是对象,因此它们不会自动装箱。
- 泛型不能使用基元作为类型参数
对于您的示例,以下是我的假设:
在3中,自动装箱发生在 类型参数, ,但返回的数组不会发生这种情况
在4中,自动装箱发生在 类型参数, ,但不会发生 方法参数, ,所以事实上 int[]
已生成,但是 Integer[]
是期待
在类型参数的情况下自动装箱可能不完全正确 自动装箱, ,但是是具有相同想法的东西。
更新: 你的第二个例子没有任何问题。 int.class
是一个 Class
, ,所以编译器没有理由拒绝它。
我同意原来的海报。这太疯狂了。为什么我不能使用原始的与通用的?这可能不是问题的编译器,但它是语言的问题。根本就是错误的,从通用跳过基本类型。
有关这样的:
intArray =(INT [])Array.newInstance(int.class,0);
int.class只是一类对象。所以,它的确定传了过来。 “INT”是一种类型,是因为它显然是原始它也不行。不是说这是“最好”的方式来创建的语言,只是坚持的语言。
这是如此疯狂,我不能创建用于使用通用基元的存储器(阵列)的分配的包装。如果使用对象,如此臃肿的庞大收藏是一种浪费。谁创造了Java语言/机器的人显然在他们的大脑有点限制。他们可以这样做不对的第一时间,但修复它需要十年,而不是这样做的权利。