Question Pourquoi le type de Java n'efface-t-il pas cela?


public class Test {
    public static class Nested<T> {
        public T val;
        Nested(T val) { this.val = val; }
    }
    public static void main(String[] args) {
        Nested<Integer> a = new Nested<Integer>(5);
        Nested<Integer> b = new Nested<Integer>(2);
        Integer diff = a.val - b.val;
    }
}

Le code ci-dessus fonctionne bien. Cependant, si j'ajoute une méthode à Nested:

T diff(Nested<T> other) { return this.val - other.val; }

J'ai une erreur de compilation:

operator - cannot be applied to T,T

Cela a du sens pour moi. Le type de T étant effacé à l'exécution, Java ne peut pas appliquer un opérateur uniquement défini pour certaines classes comme Integer. Mais pourquoi a.val - b.val  travail?

Modifier:

Beaucoup de bonnes réponses. Merci tout le monde. L’essentiel, si je comprends bien, est que le compilateur peut ajouter des casts à Integer dans a.val - b.val parce qu'il sait a et b ont été instanciés comme Nested<Integer>. Cependant, depuis this.val - other.val se produit à l'intérieur du corps d'une définition de fonction générique (où T pourrait toujours être n'importe quoi), le compilateur ne peut pas ajouter les castings qu'il serait nécessaire de faire "-"work. Cela conduit à une question plus intéressante, à savoir, si le compilateur Java était capable de s'incliner, serait-il possible qu'une fonction générique comme diff fonctionne?


13
2017-08-03 02:31


origine


Réponses:


La différence entre les deux est de savoir si vous êtes dans une méthode générique ou si vous êtes en dehors de celle-ci.

Vous avez tout à fait raison que dans la méthode T n'est pas connu pour être un Integer, donc opérateur moins - ne peut pas être appliqué. Cependant, quand vous êtes dans main(), en dehors de la méthode générique, le compilateur sait que vous avez instancié Nested avec Integer, donc il sait très bien comment appliquer l'opérateur. Même si la mise en œuvre du générique a effacé le type pour produire le code pour Nested<T>, le compilateur ne pense pas à a et b en terme de Nested<T>: il a suffisamment de connaissances pour insérer une distribution appropriée, décocher les résultats et appliquer le moins - opérateur.


9
2017-08-03 02:35



Vous obtenez une erreur de compilation, pas une erreur d'exécution.

public static void main(String[] args) {
    Nested<Integer> a = new Nested<Integer>(5);
    Nested<Integer> b = new Nested<Integer>(2);
    Integer diff = a.val - b.val;
}

Ici, compilateur sait que les deux T sont Integer. Vous venez de déclarer <Integer>.

T diff(Nested<T> other) { return this.val - other.val; }

Ici, le compilateur n'est pas certain de T. Cela pourrait être n'importe quoi. Et, opérateur numérique uniquement - n'est pas autorisé pour n'importe quoi.


8
2017-08-03 02:36



a.val - b.val fonctionne car il est validé par le compilateur, pas en runtime. Le compilateur "voit" que vous utilisez <Integer> et il compile et exécute Ok, dans l'exécution il n'y a pas de problème même avec l'effacement car le compilateur l'a déjà validé.


1
2017-08-03 02:34



Comme le code ne réside pas dans Nested, le type est connu. Le compilateur peut clairement voir que a.val - b.val est un entier moins un entier qui peut être mis en boîte automatique. Le compilateur le réécrit essentiellement en

Integer diff = Integer.valueOf(((Integer) a.val).intValue() - ((Integer) b.val).intValue())

Les appels .intValue et .valueOf proviennent de l'auto-boxing et de l'auto-unboxing.

Les fontes de type sont sécurisées pour le compilateur car vous avez utilisé un type paramétré imbriqué.

Vrai, techniquement, un pourrait être quelque chose d'autre, comme un objet Calendar, car le type est inconnu à l'exécution. Mais si vous utilisez des génériques, le compilateur espère que vous ne faites rien de stupide pour le contourner. Par conséquent, si a.val ou b.val était autre chose qu'Integers, une exception ClassCastException serait lancée à l'exécution.


1
2017-08-03 02:39



Parce que l'appel de la méthode est à l'exécution et a.val - b.val est vérifié au moment de la compilation.

  • Dans le premier cas, le compilateur sait que le type est Integer et - l'opération est autorisée pour les entiers.
  • Dans le second cas, le type de T n'est pas connu du compilateur à l'avance, il n'est donc pas sûr que - l'opération est valide ou non. D'où l'erreur de compilation.

Considérez que nous utilisons la méthode comme diff(Nested<Book> other) il n'y a donc aucun moyen de soustraire un livre aux autres.


1
2017-08-03 02:35