简体   繁体   English

尝试在泛型中使用基本类型时出现奇怪的编译时行为

[英]Weird compile-time behavior when trying to use primitive type in generics

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

I'm trying to fully understand how generics works in Java. 我试图全面了解泛型在Java中的工作方式。 Things get a bit weird for me in the 3rd assignment in the above snippet: the compiler is complaining that Integer[] cannot be converted to int[] . 在上面的代码段的第3个分配中,事情变得有些奇怪:编译器抱怨Integer[]无法转换为int[] The statement is 100% true, of course, but I'm wondering WHY the compiler is making this complaint. 当然,该陈述是100%正确的,但是我想知道为什么编译器会发出此投诉。

If you comment that line, and follow the compiler's "suggestion" as in the 4th assignment, the compiler is actually satisfied!!! 如果您注释该行,并按照第4项作业中的说明执行编译器的“建议”, 则编译器实际上很满意!!! NOW the code compiles just fine! 现在代码可以编译了! Which is crazy, of course, since like the run time behavior suggests, int[] cannot be converted to Object[] (which is what T[] is type-erased into at run time). 当然,这很疯狂,因为就像运行时行为所暗示的那样,无法将int[]转换为Object[] (这是T[]在运行时被类型擦除的对象)。

So my question is: why is the compiler "suggesting" that I assign to Integer[] instead for the 3rd assignment? 所以我的问题是:为什么编译器“建议”我分配给Integer[]而不是第三次分配? How does the compiler reason to arrive to that (erroneous!) conclusion? 编译器如何得出该结论(错误!)?


There is a lot of confusion in the two answers so far, so I created another baffling example to illustrate the underlying issue here: 到目前为止,这两个答案存在很多混乱,因此我创建了另一个令人困惑的示例来说明此处的潜在问题:

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"
    }
}

Am I the only one that thinks it's absolutely crazy that the compiler lets the above code compiles? 我是唯一认为编译器让上述代码进行编译绝对疯狂的人吗?

It wouldn't be unreasonable to print c.getSuperclass().getName() in the above code, for example, since I specified that T extends Number . 例如,在上面的代码中打印c.getSuperclass().getName()并非没有道理,因为我指定T extends Number Of course now getName() will throw NullPointerException when c == int.class , since c.getSuperclass() == null . 当然,现在由于c.getSuperclass() == null ,所以当c == int.classgetName()将抛出NullPointerException

And to me, that's a very good reason to reject the code from compiling in the first place. 对我来说,这是一个很好的理由,它一开始就拒绝编写代码。


Perhaps the ultimate craziness: 也许最终的疯狂:

    int.class.cast(null);

That code compiles AND runs fine. 该代码可以编译运行良好。

The type of int.class is Class<Integer> , so genericArrayNewInstance() would be inferred to return a Integer[] . int.class的类型为Class<Integer> ,因此将推断genericArrayNewInstance()以返回Integer[] But the function actually creates an int[] , so it would have a class cast exception when it is returned. 但是该函数实际上创建了一个int[] ,因此返回时将具有类强制转换异常。 Basically, the cast to T[] inside the function is not legitimate in this case, because int[] is not a T[] (primitives can't be used in type variables). 基本上,在这种情况下,在函数内部强制转换为T[]是不合法的,因为int[]不是T[] (原语不能用于类型变量中)。 You cannot handle primitive array types generically; 您不能通用地处理基本数组类型; so you either have to have your method just return type Object , or you have to make separate methods for reference types and for primitive types. 因此,要么必须让您的方法只返回Object类型,要么就必须为引用类型和原始类型创建单独的方法。

A few points: 几点:

  1. primitives are autoboxed to their object counterparts (wrappers) when needed 在需要时, 将原语自动装箱到其对应的对象(包装器)
  2. primitive arrays are objects, so they are not autoboxed. 基本数组是对象,因此不会自动装箱。
  3. generics cannot use primitives as type parameters 泛型不能使用基元作为类型参数

For your examples, here are my assumptions: 对于您的示例,这是我的假设:

in 3 the autoboxing happens to the type parameter , but doesn't happen to the returned array 在3中,自动装箱发生在type参数上 ,但不会发生在返回的数组上
in 4 the autoboxing happens to the type parameter , but doesn't happen to the method argument , so in fact an int[] is generated, but Integer[] is expected 在4中,自动装箱发生在type参数上 ,而方法参数没有发生,因此实际上会生成int[] ,但是期望使用Integer[]

Autoboxing in the case of type parameter might not be exactly autoboxing , but is something with the same idea. 在类型参数的情况下, 自动装箱可能不是完全自动装箱 ,但具有相同的想法。

Update: your 2nd example has nothing wrong. 更新:您的第二个示例没有错。 int.class is a Class , so the compiler has no reason to reject it. int.class是一个Class ,因此编译器没有理由拒绝它。

I agree with the original poster. 我同意原始海报。 This is crazy. 这太疯狂了。 Why can't I use primitive with generic? 为什么不能将原始与泛型一起使用? This may not be compiler problem, but it's the language's issue. 这可能不是编译器问题,但这是语言的问题。 Simply wrong to skip primitive types from generic. 从泛型中跳过基本类型完全是错误的。

For this: 为了这:

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

int.class is just a Class object. int.class只是一个Class对象。 So it's ok to pass over. 因此,可以通过。 "int" is a type, so it's not ok because it's clearly primitive. “ int”是一种类型,因此不正确,因为它显然是原始的。 Not to say that's the "best" way to create the language, just to adhere to the language. 并不是说这是创建语言的“最佳”方法,只是坚持该语言。

This is so crazy that I can't create a wrapper for memory (array) allocation of primitives using generic. 这太疯狂了,以至于我无法使用泛型为原语的内存(数组)分配创建包装器。 If using objects, that is so bloat for a huge collections that is wasteful. 如果使用对象,那么对于庞大的集合而言实在是肿,这是浪费的。 People who created the Java language/machine clearly have a bit limit in their brain. 创建Java语言/机器的人的大脑显然有些局限。 They can do it wrong the 1st time, but fixing it takes a decade, and not doing it right. 他们可能第一次做错了,但是修复起来需要十年,而且做得不好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Java泛型'不兼容类型'编译时错误 - Java Generics 'Incompatible Type' Compile-Time Error 使用具有多个边界的泛型时,编译器不会显示编译时错误 - Compiler doesn't print a compile-time error when use Generics with Multiple Bounds 仅当类为泛型时才出现奇怪的编译时错误 - Weird compile-time error only when class is generic 编译时的原始变量类型 - Primitive variable type in compile time 是否有编译时依赖注入工具支持 kotlin(或 java)的 generics? - Is there a compile-time dependency injection tool that supports generics for kotlin (or java)? 为什么这个涉及泛型的编译时出现错误? - Why does a compile-time error appears for this one involving Generics? 确定编译时多包异常类型 - Determining compile-time multicatch exception type 什么时候在Java / Gradle中使用Run-time而不是Compile-time依赖? - When is a time to use Run-time over Compile-time dependencies in Java/Gradle? 尝试转换 java 枚举时,注释参数必须是编译时常量 - An annotation argument must be a compile-time constant when trying to convert java enum 用于区分运行时类型和编译时类型的 Java 术语 - Java terminology for differentiating runtime type from compile-time type
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM