web-dev-qa-db-fra.com

C # vs Java génériques

J'ai entendu dire que l'implémentation Java de Generics n'est pas aussi bonne que l'implémentation C #. Dans la mesure où la syntaxe est similaire, qu'est-ce qui est insalubre dans l'implémentation de Java, ou s'agit-il d'un point de vue religieux?

114
johnc

lien de streloksi fait un excellent travail pour briser les différences. Le résumé rapide et sale est cependant ...

En termes de syntaxe et d'utilisation. La syntaxe est à peu près la même entre les langues. Quelques caprices ici et là (notamment dans les contraintes). Mais fondamentalement, si vous pouvez lire l'un, vous pouvez probablement lire/utiliser l'autre.

La plus grande différence réside cependant dans la mise en œuvre.

Java utilise la notion d'effacement de type pour implémenter des génériques. En bref, les classes compilées sous-jacentes ne sont pas réellement génériques. Ils compilent vers le bas pour Object et transtypent. En effet Java sont un artefact de temps de compilation et peuvent facilement être subvertis au moment de l'exécution.

C # d'autre part, en vertu du CLR, implémentez les génériques tout au long du code octet. Le CLR a pris plusieurs changements de rupture afin de prendre en charge les génériques dans 2.0. Les avantages sont des améliorations de performances, une vérification de sécurité de type approfondie et une réflexion.

Encore une fois, le fourni lien a une ventilation beaucoup plus approfondie, je vous encourage à lire

157
JaredPar

La différence se résume à une décision de conception prise par Microsoft et Sun.

Generics in Java est implémenté via type erasure par le compilateur, ce qui signifie que la vérification de type a lieu au moment de la compilation et que les informations de type sont supprimées. Cette approche a été adoptée pour garder le code hérité compatible avec le nouveau code utilisant des génériques:

À partir de Java Tutorials, Generics: Type Erasure :

Lorsqu'un type générique est instancié, le compilateur traduit ces types par une technique appelée effacement de type - un processus où le compilateur supprime toutes les informations liées aux paramètres de type et aux arguments de type dans une classe ou une méthode. L'effacement de type permet aux applications Java qui utilisent des génériques de maintenir la compatibilité binaire avec les bibliothèques et applications Java créées avant les génériques).

Cependant, avec génériques en C # (.NET) , il n'y a pas d'effacement de type par le compilateur et les vérifications de type sont effectuées pendant l'exécution. Cela présente l'avantage que les informations de type sont conservées dans le code compilé.

De Wikipédia:

Ce choix de conception est mis à profit pour fournir des fonctionnalités supplémentaires, telles que permettre la réflexion avec la préservation des types génériques, ainsi que pour atténuer certaines des limitations de l'effacement (comme l'impossibilité de créer des tableaux génériques). Cela signifie également qu'il n'y a aucun impact sur les performances des lancements d'exécution et des conversions de boxe normalement coûteuses.

Plutôt que de dire "les génériques .NET valent mieux que Java génériques"), il convient d'examiner la différence d'approche pour implémenter les génériques. En Java, il semble que la préservation de la compatibilité était une priorité élevée, tandis que dans .NET (lors de l'introduction à la version 2.0), la réalisation de tous les avantages de l'utilisation des génériques était une priorité plus élevée.

35
coobird

On trouve également cette conversation avec Anders Hejlsberg qui peut aussi être intéressante. Pour résumer les points qu'Anders Hejlsberg a fait avec quelques notes supplémentaires: Les génériques Java ont été créés pour une compatibilité maximale avec la JVM existante qui a conduit à peu de choses étranges par rapport à l'implémentation que vous voyez en C #:

  • L'effacement de type force l'implémentation à représenter chaque valeur générique paramétrée comme Object. Alors que le compilateur fournit des conversions automatiques entre Object et des types plus spécifiques, il ne supprime pas l'impact négatif des conversions de type et de la boxe sur les performances (par exemple Object est transtypé en un type spécifique MyClass ou int devait être encadré dans Integer, ce qui serait encore plus grave pour C # /. NET si ils ont suivi l'approche d'effacement des types en raison des types de valeurs définis par l'utilisateur). Comme Anders l'a dit: "vous n'obtenez aucune efficacité d'exécution" (que les génériques réifiés permettent en C #)

  • L'effacement de type rend les informations disponibles au moment de la compilation non accessibles pendant l'exécution . Quelque chose qui était List<Integer> devient juste un List sans aucun moyen de récupérer le paramètre de type générique à l'exécution. Cela rend difficile la construction de scénarios de réflexion ou de génération de code dynamique autour de Java génériques. Plus récents SO answer montre un moyen de le contourner via des classes anonymes. Mais sans astuces, quelque chose comme la génération de code à l'exécution via une réflexion qui récupère les éléments d'une instance de collection et les place dans une autre instance de collection peut échouer à l'exécution lors de l'exécution de code généré dynamiquement: la réflexion n'aide pas à détecter le décalage dans List<Double> contre List<Integer> dans ces situations.

Mais +1 pour la réponse reliant à Jonathan Pryor article de blog .

5
IgorK