84 votes

Que sont les génériques réifiés ? Comment résolvent-ils les problèmes d'effacement de type et pourquoi ne peuvent-ils pas être ajoutés sans changements majeurs ?

J'ai lu Neal Gafter 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 étant donné l'état actuel de Java, de la JVM et de l'API Collections existante ? Ne pourraient-elles pas remplacer les implémentations existantes dans une future version de Java de manière à préserver la rétrocompatibilité ?

A titre d'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 sur le type.

Pourquoi la préservation des informations sur les classes génériques nécessiterait-elle des modifications du langage plutôt qu'une simple modification de l'implémentation de la JVM ?

Qu'est-ce que je ne comprends pas ?

46voto

ghempton Points 2872

Le point essentiel est que les génériques réifiés ont un support dans le compilateur pour préserver les informations de type, alors que les génériques effacés n'en ont pas. D'après ce que je sais, l'intérêt d'avoir un effacement de type en premier lieu était de permettre une compatibilité ascendante (par exemple, les JVM de version inférieure pouvaient toujours comprendre les classes génériques).

Vous pouvez ajouter explicitement les informations de type dans l'implémentation, comme vous l'avez fait ci-dessus, mais cela nécessite du code supplémentaire à chaque fois que la liste est utilisée, et c'est plutôt désordonné à mon avis. De plus, dans ce cas, vous n'avez toujours pas de vérification de type à l'exécution pour toutes les méthodes de la liste, à moins que vous n'ajoutiez les vérifications vous-même, mais les génériques réifiés garantiront les types à l'exécution.

8 votes

Le code source et objet écrit pour les anciennes JVM peut fonctionner sur les nouvelles JVM sans aucun changement (le code source aura des problèmes avec "enum" comme identifiant, mais ce n'est pas de la générique en tant que telle).

2 votes

"Les JVM de version inférieure pourraient encore comprendre les classes génériques" <-- Je pense que vous obtiendrez un UnsupportedClassVersionError lorsque vous essayerez d'exécuter un programme compilé avec une version plus récente de JDK dans une version plus ancienne de JRE/JVM. Le fait devrait donc être ce que Tom Hawtin et Richard Gomes ont dit, c'est-à-dire le contraire.

22voto

Richard Gomes Points 1386

Contrairement à ce que pensent la majorité des développeurs Java, il est possible de conserver les informations de type au moment de la compilation et de les récupérer au moment de l'exécution, bien que 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 devez 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.

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 à cette technique. En bref, il s'agit d'un sujet obscur et la technique (ou le modèle, si vous préférez) semble étrangère à la majorité des développeurs Java.

Exemple de code

L'article que j'ai mentionné ci-dessus contient des liens vers le code source qui permet d'exercer l'idée.

12 votes

Cela n'a rien à voir avec les génériques réifiés. Oui, les génériques dans déclarations de classes comme les types dans les paramètres génériques, les types de champs, les types dans les signatures de méthodes, le type de superclasse, le type de classe englobante des classes internes (il n'y a rien de spécial pour les classes anonymes) sont stockés dans le bytecode parce que les autres classes qui utilisent cette classe et qui n'ont que le fichier .class doivent connaître ces choses pour appliquer les génériques. Toutefois, la discussion sur les génériques réifiés et non réifiés porte sur le type générique des classes internes. objets d'exécution ce qui n'a rien à voir avec ce dont vous parlez ci-dessus.

1 votes

@newacct : Oui, c'est le cas. Oui, les classes internes sont spéciales du point de vue où elles préservent des informations de compilation qui peuvent être récupérées à l'exécution. Par définition, lorsque vous pouvez, à l'exécution, récupérer les informations de type de compilation des classes génériques... qu'avez-vous ?

7 votes

Vous pouvez également récupérer les informations de compilation sur la superclasse, les superinterfaces, les champs, les méthodes, etc. pour N'IMPORTE QUELLE classe.

16voto

Harper Shelby Points 13395

IIRC (et sur la base du lien), les génériques Java ne sont qu'un sucre syntaxique pour la technique existante consistant à utiliser une collection d'objets et à effectuer un casting dans les deux sens. Il est plus sûr et plus simple d'utiliser les génériques Java, puisque le compilateur peut effectuer les contrôles pour vous afin de vérifier que vous maintenez compiler -Sécurité de type temps. Le temps d'exécution, cependant, est un problème entièrement différent.

Les génériques .NET, en revanche, créent réellement de nouveaux types - un List<String> en C# est un type différent de celui d'un List<Int> . En Java, sous les couvertures, ils sont la même chose - une List<Object> . Cela signifie que si vous avez un exemplaire de chacun d'entre eux, vous ne pouvez pas les regarder au moment de l'exécution et voir ce qu'ils étaient déclarés - seulement ce qu'ils sont maintenant.

Les génériques réifiés changeraient cela, en donnant aux développeurs Java les mêmes capacités que celles qui existent actuellement dans .NET.

2 votes

J'ai mis tes listes entre guillemets pour que les types apparaissent.

0 votes

@David - merci. Autant de fois que j'ai fait ça pour quelqu'un d'autre, vous pensez que je m'en souviendrais...

1 votes

Vous pouvez imiter une sorte de générique réifié avec des classes anonymes, c'est-à-dire que les informations de type sont disponibles à l'exécution dans cette circonstance. Bien que cela puisse être fait, son applicabilité est très limitée et la technique est très délicate. Les génériques réifiés sont un grand manque ; ceci est certainement vrai.

4voto

n3rd Points 3292

Je ne suis pas un expert en la matière, mais d'après ce que j'ai compris, les informations sur les types sont perdues au moment de la compilation. Contrairement au C++, Java n'utilise pas de système de template, la sécurité des types est entièrement réalisée par le compilateur. Au moment de l'exécution, une liste est en fait une liste, toujours.

Je pense donc qu'un changement dans la spécification du langage est nécessaire en raison du fait que les informations sur les types ne sont pas disponibles pour la JVM. parce que ce n'est pas là .

0 votes

Ceci est vrai dans la plupart des cas, sauf lorsque vous avez des classes anonymes. Je veux dire : vous pouvez éviter l'effacement des types lorsque vous utilisez des classes anonymes.

0 votes

Pour être un peu plus précis sur le sujet : Java ne génère pas de copies multiples de votre code, comme le fait C++ pour les templates. Java génère une seule copie sur le site de l'appelant, qui utilise des objets. Ainsi, bien que l'information sur le type au moment de l'exécution soit disponible dans le cas des classes anonymes, c'est tout ce que vous avez : l'information elle-même. Le bytecode ne cible pas les types réels que vous passerez au moment de l'exécution.

0voto

VinKrish Points 340

L'explication la plus simple à laquelle je peux penser est qu'ils ont délibérément choisi la simplicité plutôt que le gonflement du code, à cause de l'effacement après la compilation du code, il n'y a qu'une seule implémentation d'une classe générique et de ses sous-types.

Le C++, quant à lui, crée une version distincte du code pour chaque mise en œuvre.

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