267 votes

Génériques Java - effacement des types - quand et comment ?

J'ai lu sur l'effacement des types de Java sur le site de Sun .

Quand l'effacement des caractères se produit-il ? Au moment de la compilation / de l'exécution : lorsque la classe est chargée / de l'exécution : lorsque la classe est instanciée ?

De nombreux sites (y compris le tutoriel Sun mentionné ci-dessus) affirment que l'effacement des types se produit au moment de la compilation. Si les informations de type sont complètement supprimées au moment de la compilation, comment le JDK vérifie-t-il la compatibilité des types lorsqu'une méthode utilisant des génériques est invoquée sans informations de type ou avec des informations de type erronées.

Considérons l'exemple suivant : disons que la classe A possède une méthode empty(Box<T extends Number> b) . Nous compilons A.java et obtenons le fichier de classe A.class. Maintenant nous créons une autre classe B qui invoque la méthode empty avec un argument non paramétré - empty(new Box()) . Si nous compilons B.java avec A.class dans le classpath, javac est assez intelligent pour émettre un avertissement. Donc A.class a un certain type d'informations qui y sont stockées.

Je pense que l'effacement des types se produit lorsque la classe est chargée, mais ce n'est qu'une supposition. Est-ce que quelqu'un ici en est sûr ?

271voto

Jon Skeet Points 692016

L'effacement de type s'applique à l utiliser de génériques. Il y a définitivement des métadonnées dans le fichier de classe pour dire si oui ou non une méthode/type est générique, et quelles sont les contraintes, etc. Mais lorsque les génériques sont utilisé ils sont convertis en contrôles au moment de la compilation et en cascades au moment de l'exécution. Donc ce code :

List<String> list = new ArrayList<String>();
list.add("Hi");
String x = list.get(0);

est compilé en

List list = new ArrayList();
list.add("Hi");
String x = (String) list.get(0);

Au moment de l'exécution, il n'y a aucun moyen de savoir que T=String pour l'objet liste - cette information a disparu.

... mais le List<T> L'interface elle-même se présente toujours comme étant générique.

EDIT : Juste pour clarifier, le compilateur conserve l'information sur l'option variable être un List<String> - mais vous ne pouvez toujours pas découvrir que T=String pour l'objet liste lui-même.

108voto

Richard Gomes Points 1386

Le compilateur est responsable de la compréhension des classes génériques au moment de la compilation. Le compilateur est également responsable de l'élimination de cette "compréhension" des classes génériques, dans un processus que nous appelons effacement de type . Tout se passe au moment de la compilation.

Note : Contrairement à ce que pensent la majorité des développeurs Java, il est possible de conserver les informations sur les types au moment de la compilation et de les récupérer au moment de l'exécution, mais de manière très limitée. En d'autres termes : Java fournit des génériques réifiés de manière très limitée. .

Concernant l'effacement des types

Remarquez qu'au moment de la compilation, le compilateur dispose d'informations complètes sur les types, mais que ces informations sont intentionnellement abandonnées. en général lorsque le code binaire est généré, dans un processus connu sous le nom de effacement de type . Cette façon de faire est due à des problèmes de compatibilité : L'intention des concepteurs du langage était de fournir une compatibilité totale du code source et une compatibilité totale du code binaire entre les versions de la plate-forme. S'il était implémenté différemment, vous devriez recompiler vos anciennes applications lorsque vous migrez vers des versions plus récentes de la plate-forme. De la manière dont cela a été fait, toutes les signatures de méthodes sont préservées (compatibilité du code source) et vous ne devez rien recompiler (compatibilité binaire).

Concernant les génériques réifiés en Java

Si vous avez besoin de conserver les informations de type au moment de la compilation, vous devez utiliser des classes anonymes. Le fait est que, dans le cas très particulier des classes anonymes, il est possible de récupérer toutes les informations de type au moment de la compilation au moment de l'exécution, ce qui, en d'autres termes, signifie : des génériques réifiés. Cela signifie que le compilateur ne jette pas les informations de type lorsque des classes anonymes sont impliquées ; ces informations sont conservées dans le code binaire généré et le système d'exécution vous permet de récupérer ces informations.

J'ai écrit un article sur ce sujet :

http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html

Une remarque sur la technique décrite dans l'article ci-dessus est que la technique est obscure pour la majorité des développeurs. Bien qu'elle fonctionne et fonctionne bien, la plupart des développeurs se sentent confus ou mal à l'aise avec cette technique. Si vous avez une base de code partagée ou si vous prévoyez de diffuser votre code au public, je ne recommande pas la technique ci-dessus. En revanche, si vous êtes le seul utilisateur de votre code, vous pouvez profiter de la puissance que cette technique vous apporte.

Exemple de code

L'article ci-dessus contient des liens vers des exemples de code.

35voto

erickson Points 127945

Si vous avez un champ qui est un type générique, ses paramètres de type sont compilés dans la classe.

Si vous avez une méthode qui prend ou renvoie un type générique, ces paramètres de type sont compilés dans la classe.

C'est cette information que le compilateur utilise pour vous dire que vous ne pouvez pas passer un fichier de type Box<String> à la empty(Box<T extends Number>) méthode.

L'API est compliquée, mais vous pouvez inspecter ces informations de type à travers l'API de réflexion avec des méthodes telles que [getGenericParameterTypes](http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#getGenericParameterTypes()) , [getGenericReturnType](http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#getGenericReturnType()) et, pour les champs, [getGenericType](http://java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html#getGenericType()) .

Si vous avez du code qui utilise un type générique, le compilateur insère des casts si nécessaire (dans l'appelant) pour vérifier les types. Les objets génériques eux-mêmes ne sont que le type brut ; le type paramétré est "effacé". Ainsi, lorsque vous créez un new Box<Integer>() il n'y a pas d'information sur le Integer dans la classe Box objet.

FAQ d'Angelika Langer est la meilleure référence que j'ai vue pour les génériques Java.

14voto

Eugene Yokota Points 43213

Génériques en langage Java est un très bon guide sur ce sujet.

Les génériques sont implémentés par le compilateur Java compilateur comme une conversion frontale appelée effacement. Vous pouvez (presque) penser comme une traduction de source à source source à source, où la version générique version de loophole() est converti en la version non générique.

Donc, c'est au moment de la compilation. La JVM ne saura jamais quel ArrayList que vous avez utilisé.

Je recommande également la réponse de M. Skeet sur Quel est le concept d'effacement dans les génériques en Java ?

9voto

Vinko Vrsalovic Points 116138

L'effacement des types se produit au moment de la compilation. Ce que l'effacement des types signifie, c'est que l'on oublie le type générique, et non pas tous les types. En outre, il y aura toujours des métadonnées sur les types qui sont génériques. Par exemple

Box<String> b = new Box<String>();
String x = b.getDefault();

est converti en

Box b = new Box();
String x = (String) b.getDefault();

au moment de la compilation. Vous pouvez obtenir des avertissements non pas parce que le compilateur sait de quel type est le générique, mais au contraire, parce qu'il n'en sait pas assez pour ne pas garantir la sécurité des types.

En outre, le compilateur conserve les informations sur le type des paramètres d'un appel de méthode, que vous pouvez récupérer par réflexion.

Ce site guide est le meilleur que j'ai trouvé sur le sujet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X