Comment l’effacement Java affecte-t-il les tableaux génériques?

J’étudie les génériques au cours de cette période et aujourd’hui, j’ai trouvé ce mystère pour moi.

Considérons le cours factice suivant:

public class Main{ public static void main(Ssortingng[] args) { Container c = new Container(); c.getArray(); //No Exception //c.getArray().getClass(); //Exception //int a = c.getArray().length; //Exception } } class Container { T[] array; @SuppressWarnings("unchecked") Container() { array = (T[])new Object[1]; } void put(T item) { array[0] = item; } T get() { return array[0]; } T[] getArray() { return array; } } 

En raison de l’effacement, au moment de l’exécution, le type de retour T [] de la méthode getArray () est transformé en Object [], ce qui me convient parfaitement.

Si nous accédons à cette méthode telle quelle (c.getArray ()), aucune exception n’est levée, mais si nous essayons d’appeler certaines méthodes sur le tableau renvoyé, par exemple c.Array (). GetClass (), ou si nous essayons de access à un champ, par exemple c.getArray (). length, l’exception suivante est levée:

Exception dans le fil “principal” java.lang.ClassCastException: [Ljava.lang.Object; ne peut pas être converti en [Ljava.lang.Integer;

Pourquoi cette exception est-elle levée? Pourquoi n’est-il pas également lancé pour l’appel simple c.getArray ()? Pourquoi tente-t-il de transtyper Integer [] si nous appelons simplement getClass () ou si nous accédons à length? GetClass () et la longueur ne sont pas disponibles également pour Object []?

Merci d’avance pour vos nombreuses réponses (j’espère) et explicatives (j’espère aussi).

Lorsque vous effectuez une dissortingbution non contrôlée non sécurisée, elle peut ou non provoquer une exception quelque part. Vous n’êtes pas assuré d’obtenir une exception quelque part.

Dans ce cas, si vous obtenez une exception dépend de si le compilateur a inséré une conversion dans le code effacé pour convertir le résultat de l’appel à Integer[] . Dans ce cas, il semble qu’un casting ait été inséré dans les deuxième et troisième cas, mais pas dans le premier cas.

Dans chacun des trois cas, le compilateur est autorisé à insérer un transtypage (car il est autorisé à supposer que le résultat est Integer[] ou à ne pas insérer de transt (car l’expression est utilisée de manière à ne requérir que Object[] Dans ce cas, il appartient à l’implémentation du compilateur de décider.

Pourquoi ce compilateur n’insère-t-il pas un casting dans le premier cas et un cast dans les deuxième et troisième cas? Une explication évidente serait que dans le premier cas, le résultat est évidemment inutilisé, il est donc très simple de déterminer qu’un casting est inutile. Dans les deuxième et troisième cas, pour déterminer si un transtypage est inutile, il faudrait examiner la façon dont l’expression est utilisée afin de s’assurer que cela fonctionnera également avec Object[] ; et ceci est une parsing plutôt compliquée. Les auteurs du compilateur ont probablement opté pour une approche simple dans laquelle ils ne sautent la dissortingbution que lorsque le résultat est inutilisé.

Un autre compilateur peut insérer des conversions dans les trois cas. Et un autre compilateur pourrait ne pas avoir d’incantation dans les trois cas. Vous ne pouvez pas compter dessus.

La raison de cette exception est que le compilateur attend un Integer[] mais reçoit un Object[] . Il a ajouté une dissortingbution au moment de l’exécution – sur les sites d’ getArray de getArray . Ces moulages ont découvert le casting menteur, factice et sans effet de votre constructeur.

Pour que cela soit correct, il faut la classe réelle de T, afin de créer des instances.

 @SuppressWarnings("unchecked") Container(Class type) { array = (T[]) Array.newInstance(type, 10); } Container c = new Container(Integer.class); c.getArray(); Class t = c.getArray().getClass(); System.out.println(t.getName()); int a = c.getArray().length; 

Ici aussi, il rest un cast “non sécurisé” vers T[] mais ceci est inévitable car Array.newInstance est une méthode de bas niveau pour les tableaux à n dimensions comme dans:

 (double[][][][][][]) Array.newInstance(double.class, 3, 3, 3, 3, 3, 6); 

Je n’ai pas été capable de trouver l’endroit exact dans le JLS qui dit que c’est le comportement, mais je pense que la raison est quelque chose comme ceci:

L’expression:

 c.getArray().getClass(); 

est à peu près équivalent à:

 Integer[] arr = (Integer[]) c.getArray(); arr.getClass(); 

où la dissortingbution doit être ajoutée en raison de l’effacement du type. Cette dissortingbution implicite ajoute une instruction checkcast dans le bytecode, ce qui échoue avec une ClassCastException , car c.getArray() est de type Object[] .

En regardant le bytecode pour:

 static void implicit() { Container c = new Container(); c.getArray().getClass(); //Exception } static void explicit() { Container c = new Container(); Integer[] arr = (Integer[]) c.getArray(); arr.getClass(); //Exception } 

on a:

  static void implicit(); Code: 0: new #2 // class Container 3: dup 4: invokespecial #3 // Method Container."":()V 7: astore_0 8: aload_0 9: invokevirtual #4 // Method Container.getArray:()[Ljava/lang/Object; 12: checkcast #5 // class "[Ljava/lang/Integer;" 15: invokevirtual #6 // Method java/lang/Object.getClass:()Ljava/lang/Class; 18: pop 19: return static void explicit(); Code: 0: new #2 // class Container 3: dup 4: invokespecial #3 // Method Container."":()V 7: astore_0 8: aload_0 9: invokevirtual #4 // Method Container.getArray:()[Ljava/lang/Object; 12: checkcast #5 // class "[Ljava/lang/Integer;" 15: checkcast #5 // class "[Ljava/lang/Integer;" 18: astore_1 19: aload_1 20: invokevirtual #6 // Method java/lang/Object.getClass:()Ljava/lang/Class; 23: pop 24: return 

Donc, la seule différence dans la version explicit réside dans les trois instructions:

  15: checkcast #5 // class "[Ljava/lang/Integer;" 18: astore_1 19: aload_1 

Qui sont là uniquement à cause du stockage explicite de celui-ci dans une variable, pour autant que je sache.