le comportement de compilation étrange lorsque vous essayez d'utiliser le type primitif dans les génériques

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

Question

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

Je suis en train de comprendre comment fonctionne génériques en Java. Les choses deviennent un peu bizarre pour moi dans la 3ème mission dans l'extrait ci-dessus: le compilateur se plaint que Integer[] ne peut pas être converti en int[]. La déclaration est 100% vrai, bien sûr, mais je me demande Pourquoi le compilateur fait cette plainte.

Si vous commentez cette ligne, et suivez le compilateur « suggestion » de comme dans le 4ème poste, le compilateur est en fait satisfait !!! MAINTENANT le code compile très bien ! Ce qui est fou, bien sûr, puisque comme le comportement de l'exécution suggère, int[] ne peut pas être converti en Object[] (qui est ce que T[] est de type effacée en au moment de l'exécution).

Alors ma question est: pourquoi le compilateur « suggère » que je cède à la place Integer[] pour la 3ème mission? Comment la raison du compilateur ne pour arriver à cette conclusion (erronée!)?


Il y a beaucoup de confusion dans les deux réponses à ce jour, donc je créé un autre exemple déconcertant pour illustrer la question sous-jacente:

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

Suis-je le seul qui pense qu'il est absolument fou que le compilateur permet le code ci-dessus compile?

Il ne serait pas déraisonnable d'imprimer c.getSuperclass().getName() dans le code ci-dessus, par exemple, puisque je précisé que T extends Number. Bien sûr, maintenant getName() va jeter NullPointerException quand c == int.class, depuis c.getSuperclass() == null.

Et pour moi, c'est une très bonne raison de rejeter le code de compilation en premier lieu.


Peut-être la folie ultime:

    int.class.cast(null);

Ce code est compilé fonctionne très bien.

Était-ce utile?

La solution

Le type de int.class est Class<Integer>, donc genericArrayNewInstance() serait déduit de retourner un Integer[]. Mais la fonction crée en fait un int[], il aurait une exception en fonte de classe quand il est retourné. En gros, la distribution à l'intérieur de la fonction T[] n'est pas légitime dans ce cas, parce que int[] n'est pas un T[] (primitives ne peuvent pas être utilisés dans des variables de type). Vous ne pouvez pas gérer les types de tableaux primitifs génériquement; de sorte que vous devez soit avoir votre méthode juste retour Object de type, ou vous devez faire des méthodes distinctes pour les types de référence et pour les types primitifs.

Autres conseils

Quelques points:

  1. autoboxed à leur homologues d'objets (enveloppes) en cas de besoin
  2. tableaux primitifs sont des objets, donc ils ne sont pas autoboxed.
  3. génériques ne peut pas utiliser les primitives en tant que paramètres de type

Pour vos exemples, voici mes hypothèses:

dans 3 l'autoboxing arrive à paramètre de type , mais ne se produit pas au tableau retourné
dans 4 l'autoboxing arrive à paramètre de type , mais ne se produit pas à la argument de méthode , donc en fait un int[] est généré, mais Integer[] devrait

Autoboxing dans le cas du paramètre de type peut ne pas être exactement autoboxing , mais il est quelque chose avec la même idée.

Mise à jour: votre 2ème exemple n'a rien de mal. int.class est un Class, de sorte que le compilateur n'a aucune raison de le rejeter.

Je suis d'accord avec l'affiche originale. C'est fou. Pourquoi ne puis-je utiliser avec primitive générique? Cela peut ne pas être un problème de compilateur, mais il est le numéro de la langue. Tout simplement faux de sauter les types primitifs de génériques.

Pour cela:

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

int.class est juste un objet de classe. Il est donc autorisé à passer au-dessus. « Int » est un type, il est donc pas correct car il est clairement primitif. Pour ne pas dire que c'est la « meilleure » façon de créer la langue, pour adhérer à la langue.

Ceci est tellement fou que je ne peux pas créer une enveloppe pour l'allocation de mémoire (tableau) de primitives en utilisant générique. Si vous utilisez des objets, qui est ballonnement donc pour une collection énorme qui est inutile. Les gens qui ont créé le langage Java / la machine ont clairement une limite de bits dans leur cerveau. Ils peuvent le faire mal la 1ère fois, mais il faut fixer une dizaine d'années, et ne pas le faire droit.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top