comportamento in fase di compilazione strano quando si tenta di utilizzare tipo primitivo in farmaci generici

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

Domanda

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

Sto cercando di comprendere appieno come funziona farmaci generici in Java. Le cose si fanno un po 'strano per me nel 3 ° assegnazione nel frammento di sopra: il compilatore si lamenta che Integer[] non può essere convertito in int[]. L'affermazione è vera al 100%, naturalmente, ma mi chiedo PERCHE ' il compilatore sta facendo questa denuncia.

Se si commenta quella linea, e seguire "proposta" del compilatore, come nel 4 ° assegnazione, il compilatore è in realtà soddisfatti !!! ora il codice viene compilato bene ! Che è pazzo, naturalmente, dal momento che, come il tempo di esecuzione comportamento suggerisce, int[] non può essere convertito in Object[] (che è ciò che è di tipo T[]-cancellati in in fase di esecuzione).

Quindi la mia domanda è: perché il compilatore "suggerendo" che assegno a Integer[] invece per il 3 ° incarico? In che modo la ragione compilatore per arrivare a questa conclusione (mancanti!)?


C'è un sacco di confusione nei due risposte finora, così ho creato un altro esempio sconcertante per illustrare la questione di fondo qui:

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

Sono l'unico che pensa che sia assolutamente pazzesco che il compilatore permette al codice di cui sopra viene compilato?

Non sarebbe irragionevole stampare c.getSuperclass().getName() nel codice qui sopra, per esempio, dal momento che ho specificato che T extends Number. Naturalmente ora getName() getterà NullPointerException quando c == int.class, dal momento che c.getSuperclass() == null.

E per me, questo è un buon motivo per rifiutare il codice da compilare, in primo luogo.


Forse l'ultima follia:

    int.class.cast(null);

Questo codice viene compilato e funziona bene.

È stato utile?

Soluzione

Il tipo di int.class è Class<Integer>, così genericArrayNewInstance() verrebbe dedotta per restituire un Integer[]. Ma la funzione in realtà crea un int[], quindi sarebbe un'eccezione calco quando viene restituito. Fondamentalmente, il cast T[] all'interno della funzione non è legittimo in questo caso, poiché non è int[] un T[] (primitive non possono essere utilizzati in variabili di tipo). Non si può gestire tipi di array primitivi genericamente; in modo che sia necessario avere il metodo giusto di ritorno Tipo Object, o si deve fare metodi separati per i tipi di riferimento e per i tipi primitivi.

Altri suggerimenti

A pochi punti:

  1. autoboxed alla loro controparti oggetto (involucri) quando necessario
  2. array primitivi sono oggetti, quindi non sono autoboxed.
  3. farmaci generici non può utilizzare primitive come parametri di tipo

Per i vostri esempi, qui sono le mie supposizioni:

in 3 l'autoboxing accade al parametro di tipo , ma non accade alla tornata matrice
a 4 l'autoboxing accade al parametro di tipo , ma non accade al metodo argomento , di fatto viene generato un int[], ma si prevede Integer[]

Autoboxing nel caso di parametro di tipo potrebbe non essere esattamente autoboxing , ma è qualcosa con la stessa idea.

Aggiornamento: il tuo secondo esempio non ha nulla di sbagliato. int.class è un Class, in modo che il compilatore non ha motivo di respingerlo.

Sono d'accordo con il manifesto originale. Questo è pazzesco. Perché non posso usare primitiva con generica? Questo potrebbe non essere un problema del compilatore, ma è questione della lingua. Semplicemente sbagliato a saltare tipi primitivi da generico.

Per questo:

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

int.class è solo un oggetto di classe. Quindi è ok per passare sopra. "Int" è un tipo, quindi non va bene perché è chiaramente primitivo. Per non dire che è il modo "migliore" per creare il linguaggio, solo per aderire alla lingua.

Questo è così pazzo che non riesco a creare un wrapper per la memoria (array) di allocazione di primitive che utilizzano generico. Se si utilizza gli oggetti, che è così sovradimensionato per un enorme collezioni che è uno spreco. Le persone che hanno creato il linguaggio Java / macchina hanno chiaramente un limite po 'nel loro cervello. Possono farlo male il primo tempo, ma il fissaggio ci vuole un decennio, e non fare bene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top