2
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);

该代码编译运行良好。

4

3 回答 3

4

的类型int.classClass<Integer>,因此genericArrayNewInstance()会被推断为返回 a Integer[]。但是该函数实际上创建了一个int[],所以当它返回时它会有一个类转换异常。基本上,T[]在这种情况下,函数内部的强制转换是不合法的,因为int[]is not a T[](原语不能用于类型变量)。您不能通用地处理原始数组类型;所以你要么必须让你的方法只返回 type Object,要么你必须为引用类型和原始类型创建单独的方法。

于 2010-03-16T10:17:08.610 回答
3

几点:

  1. 原语在需要时自动装箱到它们的对象对应物(包装器)
  2. 原始数组是对象,因此它们不会自动装箱。
  3. 泛型不能使用原语作为类型参数

对于您的示例,这是我的假设:

在 3 中,自动装箱发生在类型参数上,但不会发生在返回的数组
中 4 自动装箱发生在类型参数上,但不会发生在方法参数上,所以实际上int[] 生成了 an,但是Integer[]是预期的

类型参数的自动装箱可能不完全是自动装箱,但具有相同的想法。

更新:你的第二个例子没有错。int.class是 a Class,所以编译器没有理由拒绝它。

于 2010-03-16T10:53:39.597 回答
0

我同意原始海报。这太疯狂了。为什么我不能将原始与泛型一起使用?这可能不是编译器的问题,而是语言的问题。从泛型中跳过原始类型是完全错误的。

为了这:

intArray = (int[]) Array.newInstance(int.class, 0);

int.class 只是一个 Class 对象。所以跳过就好了。"int" 是一种类型,所以它是不行的,因为它显然是原始的。并不是说这是创建语言的“最佳”方式,只是坚持语言。

这太疯狂了,以至于我无法使用泛型为基元的内存(数组)分配创建包装器。如果使用对象,那么对于一个巨大的集合来说太臃肿了,这是浪费的。创建 Java 语言/机器的人显然在他们的大脑中有一点限制。他们可以第一次做错,但修复它需要十年时间,而且做得不对。

于 2010-12-10T14:45:10.323 回答