J'ai lu Neal Gafter's blog sur le sujet et je ne suis toujours pas clair sur un certain nombre de points.
Pourquoi n'est-il pas possible de créer des implémentations de l'API Collections qui préservent les informations de type compte tenu de l'état actuel de Java, de la JVM et de l'API Collections existante? Ne pourraient-ils pas remplacer les implémentations existantes dans une future version de Java d'une manière où la compatibilité descendante est préservée?
Par exemple:
List<T> list = REIList<T>(T.Class);
Où REIList est quelque chose comme ça:
public REIList<T>() implements List {
private Object o;
private Class klass;
public REIList(Object o) {
this.o = o;
klass = o.getClass();
}
... the rest of the list implementation ...
Et les méthodes utilisent Object o et Class klass pour obtenir les informations de type.
Pourquoi la préservation des informations de classe génériques nécessiterait-elle des changements de langage plutôt qu'un simple changement d'implémentation JVM?
Qu'est-ce que je ne comprends pas?
Le fait est que les génériques réifiés prennent en charge dans le compilateur la conservation des informations de type, contrairement aux génériques effacés. AFAIK, l'intérêt d'avoir l'effacement de type en premier lieu était de permettre la rétrocompatibilité (par exemple, les machines virtuelles Java de version inférieure pouvaient toujours comprendre les classes génériques).
Vous pouvez explicitement ajouter les informations de type dans l'implémentation, comme vous l'avez ci-dessus, mais cela nécessite du code supplémentaire chaque fois que la liste est utilisée, et c'est assez compliqué à mon avis. De plus, dans ce cas, vous n'avez toujours pas de vérification du type d'exécution pour toutes les méthodes de liste, sauf si vous ajoutez les vérifications vous-même, mais les génériques réifiés garantiront les types d'exécution.
Contrairement aux croyances de la majorité des développeurs Java développeurs, il est possible de conserver les informations de type à la compilation et de récupérer ces informations lors de l'exécution, malgré de manière très restreinte. En d'autres termes: Java fournit des génériques réifiés de manière très restreinte .
Notez qu'au moment de la compilation, le compilateur dispose d'informations de type complet mais que ces informations sont intentionnellement supprimées en général lorsque le code binaire est généré, dans un processus appelé effacement de type . Cela se fait de cette façon en raison de problèmes de compatibilité: l'intention des concepteurs de langage était de fournir une compatibilité complète du code source et une compatibilité complète du code binaire entre les versions de la plate-forme. S'il était implémenté différemment, vous devrez recompiler vos applications héritées lorsque vous migrez vers des versions plus récentes de la plate-forme. De la façon dont cela a été fait, toutes les signatures de méthode sont préservées (compatibilité du code source) et vous n'avez rien à recompiler (compatibilité binaire).
Si vous devez conserver des informations de type à la compilation, vous devez utiliser des classes anonymes. Le point est le suivant: dans le cas très particulier des classes anonymes, il est possible de récupérer des informations complètes de type à la compilation lors de l'exécution ce qui signifie, en d'autres termes: des génériques réifiés.
J'ai écrit un article sur ce sujet:
http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html
Dans l'article, je décris comment nos utilisateurs ont réagi à la technique. En un mot, c'est un sujet obscur et la technique (ou le motif, si vous préférez) semble étrangère à la majorité des développeurs Java.
L'article que j'ai mentionné ci-dessus contient des liens vers le code source qui exerce l'idée.
IIRC (et basé sur le lien), Java ne sont que du sucre syntaxique pour la technique existante d'utilisation d'une collection d'objets et de transtypage en arrière. Il est plus sûr et plus simple d'utiliser le Java, car le compilateur peut effectuer les vérifications pour vous afin de vérifier que vous maintenez compiler - la sécurité du type de temps. Le temps d'exécution, cependant, est un problème complètement différent.
Les génériques .NET, en revanche, créent en fait de nouveaux types - un List<String>
en C # est d'un type différent d'un List<Int>
. En Java, sous les couvertures, c'est la même chose - un List<Object>
. Cela signifie que si vous en avez un de chaque, vous ne pouvez pas les regarder au moment de l'exécution et voir à quoi ils ont été déclarés - seulement ce qu'ils sont maintenant.
Les génériques réifiés changeraient cela, donnant aux développeurs Java les mêmes capacités qui existent maintenant dans .NET.
Je ne suis pas un expert sur le sujet, mais si je comprends bien, les informations de type sont perdues au moment de la compilation. Contrairement à C++, Java n'utilise pas de système de modèle, la sécurité des types est entièrement obtenue via le compilateur. Au moment de l'exécution, une List est en fait toujours une List.
Donc, mon avis est qu'un changement dans la spécification du langage est nécessaire en raison du fait que les informations de type ne sont pas disponibles pour la JVM car elles ne sont pas là.