extraño comportamiento en tiempo de compilación al intentar utilizar tipo primitivo de los genéricos

StackOverflow https://stackoverflow.com/questions/2453359

Pregunta

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

Estoy tratando de entender completamente cómo funciona genéricos en Java. Las cosas se ponen un poco raro para mí en la tercera asignación en el fragmento anterior: el compilador se queja de que Integer[] no se puede convertir a int[]. La declaración es 100% cierto, por supuesto, pero me pregunto ¿Por qué el compilador está haciendo esta queja.

Si usted comenta esa línea, y sigue "sugerencia" del compilador como en el cuarto de asignación, el compilador es realmente satisfecho !!! ahora el código se compila bien ! Que es una locura, por supuesto, ya que al igual que el tiempo de ejecución comportamiento sugiere, int[] no se puede convertir a Object[] (que es lo T[] es del tipo que se ha borrado en en tiempo de ejecución).

Así que mi pregunta es: ¿por qué el compilador "sugiriendo" que le asigno a Integer[] lugar para la tercera asignación? ¿Cómo funciona el compilador razón para llegar a eso (! Errónea) conclusión?


Hay mucha confusión en las dos respuestas hasta el momento, así que creé otro ejemplo desconcertante para ilustrar la cuestión de fondo aquí:

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

¿Soy el único que piensa que es absolutamente loco que el compilador permite el código anterior compila?

No sería razonable para imprimir c.getSuperclass().getName() en el código anterior, por ejemplo, ya he especificado que T extends Number. Por supuesto, ahora getName() tirará NullPointerException cuando c == int.class, ya c.getSuperclass() == null.

Y para mí, eso es una muy buena razón para rechazar el código de compilación en el primer lugar.


Tal vez la locura final:

    int.class.cast(null);

Ese código compila y funciona muy bien.

¿Fue útil?

Solución

El tipo de int.class es Class<Integer>, por lo genericArrayNewInstance() se infiere que devolver un Integer[]. Pero en realidad la función crea un int[], por lo que tendría una excepción de difusión clase cuando se devuelve. Básicamente, el reparto a T[] dentro de la función no es legítimo en este caso, porque int[] no es un T[] (primitivas no se pueden utilizar en las variables de tipo). Usted no puede manejar los tipos de matriz primitiva forma genérica; así que o bien tiene que tener su método que acabamos de regresar de tipo Object, o usted tiene que hacer que los métodos distintos para los tipos de referencia y para los tipos primitivos.

Otros consejos

A pocos puntos:

  1. autoboxed a su homólogos objeto (envolturas) cuando sea necesario
  2. matrices primitivas son objetos, por lo que no se autoboxed.
  3. genéricos no puede utilizar primitivas como los parámetros de tipo

Para sus ejemplos, aquí están mis suposiciones:

3 en la autoboxing sucede con el parámetro de tipo , pero no sucede a la matriz devuelta
4 en la autoboxing sucede con el parámetro de tipo , pero no sucede a la argumento de un método , por lo que, de hecho, se genera un int[], pero se espera Integer[]

Autoboxing en el caso del parámetro de tipo podría no ser exactamente autoboxing , pero es algo con la misma idea.

Actualización: el segundo ejemplo tiene nada de malo. int.class es un Class, por lo que el compilador no tiene ninguna razón para rechazarla.

Estoy de acuerdo con su creador original. Esto es Loco. ¿Por qué no puedo usar primitiva con genérico? Esto puede no ser un problema del compilador, pero es cuestión de la lengua. Simplemente errónea saltarse los tipos primitivos de genéricos.

Para esto:

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

int.class es sólo un objeto de clase. Así que está bien para pasar por encima. "Int" es un tipo, por lo que no es aceptable porque es claramente primitiva. No quiere decir que es la "mejor" para crear el lenguaje, sólo para adherirse a la lengua.

Esto es tan loco que no puedo crear un contenedor para la memoria (matriz) ubicación de las primitivas utilizando genérico. Si el uso de objetos, por lo que es la hinchazón por unos enormes colecciones que es un desperdicio. Las personas que crearon el lenguaje / máquina de Java tienen claramente un límite poco en su cerebro. Pueden hacerlo mal la primera vez, pero se necesita la fijación de una década, y no hacer las cosas bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top