J'ai vu à plusieurs reprises sur ce site des publications qui dénoncent l'implémentation des génériques par Java. Maintenant, je peux honnêtement dire que je n’ai eu aucun problème à les utiliser. Cependant, je n'ai pas tenté de créer moi-même une classe générique. Alors, quels sont vos problèmes avec le support générique de Java?
49
Réponses:
L'implémentation générique de Java utilise le type effacement . Cela signifie que vos collections génériques fortement typées sont en réalité de type
Object
au moment de l'exécution. Cela a des conséquences sur les performances car cela signifie que les types primitifs doivent être encadrés lorsqu'ils sont ajoutés à une collection générique. Bien entendu, les avantages de la correction du type de temps de compilation l'emportent sur la stupidité générale de l'effacement du type et la focalisation obsessionnelle sur la compatibilité ascendante.la source
TypeLiteral
google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/…new ArrayList<String>.getClass() == new ArrayList<Integer>.getClass()
template<T> T add(T v1, T v2) { return v1->add(v2); }
une méthode vraiment générique de créer une fonction qui comporteadd
deux choses, peu importe ce que sont ces choses, elles doivent simplement avoir une méthode nomméeadd()
avec un paramètre.Add
exemple que j’ai donné, il est impossible de savoir à l’avance à quelles classes il peut être appliqué, ce qui est un inconvénient.Notez que les réponses déjà fournies se concentrent sur la combinaison du langage Java, de la machine virtuelle Java et de la bibliothèque de classes Java.
Il n’ya rien de mal avec les génériques de Java en ce qui concerne le langage Java. Comme décrit dans Génériques C # vs Java, les génériques Java sont assez bien au niveau de langue 1 .
Ce qui est sous-optimal, c'est que la JVM ne prend pas directement en charge les génériques, ce qui a plusieurs conséquences majeures:
Je suppose que la distinction que je fais est de nature pédante, car le langage Java est très largement compilé avec JVM comme cible et la bibliothèque de classes Java comme bibliothèque principale.
1 À l'exception peut-être des caractères génériques, qui sont supposés rendre l'inférence de type indécidable dans le cas général. C'est une différence majeure entre les génériques C # et Java, qui n'est pas du tout mentionnée très souvent. Merci Antimony.
la source
8675309
) et en créer un type unique, avecZ8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>
des membres différents de tout autre type. En C ++, tous les types pouvant être générés à partir de n'importe quelle entrée doivent être générés au moment de la compilation.La critique habituelle est le manque de réification. C'est-à-dire que les objets au moment de l'exécution ne contiennent pas d'informations sur leurs arguments génériques (bien que les informations soient toujours présentes sur les champs, la méthode, les constructeurs et la classe étendue et les interfaces). Cela signifie que vous pouvez lancer, par exemple,
ArrayList<String>
pourList<File>
. Le compilateur vous donnera des avertissements, mais il vous avertira également si vousArrayList<String>
assignez un attribut à uneObject
référence et que vous le transmettez ensuite àList<String>
. Les avantages sont que, avec les génériques, vous ne devriez probablement pas faire le casting, la performance est meilleure sans les données inutiles et, bien sûr, la compatibilité ascendante.Certaines personnes se plaignent de l'impossibilité de surcharger sur la base d'arguments génériques (
void fn(Set<String>)
etvoid fn(Set<File>)
). Vous devez utiliser de meilleurs noms de méthodes. Notez que cette surcharge ne nécessiterait pas de réification, car la surcharge est un problème statique, au moment de la compilation.Les types primitifs ne fonctionnent pas avec les génériques.
Les caractères génériques et les limites sont assez compliqués. Ils sont très utiles. Si Java préférait l'immuabilité et les interfaces tell-not-ask, les déclarations génériques du côté de la déclaration plutôt que du côté de l'utilisation seraient plus appropriées.
la source
Object cs = new char[] { 'H', 'i' }; System.out.println(cs);
vous obtenez un non-sens imprimé. Modifier le type decs
àchar[]
et vous obtiendrezHi
.Les génériques Java craignent parce que vous ne pouvez pas faire ce qui suit:
Vous n'avez pas besoin de couper toutes les classes du code ci-dessus pour que cela fonctionne comme il se doit si les génériques étaient en fait des paramètres de classe génériques.
la source
Parser
j'ai besoin de rendre le code d'usine encore plus compliqué. Tandis que si "générique" signifiait vraiment générique, il n'y aurait aucun besoin d'usines ni de tous les autres non-sens qui s'appellent des modèles de conception. C'est l'une des nombreuses raisons pour lesquelles je préfère travailler dans des langages dynamiques.les avertissements du compilateur pour les paramètres génériques manquants qui ne servent à rien rendent le langage inutilement bavard, par exemple
public String getName(Class<?> klazz){ return klazz.getName();}
Les génériques ne jouent pas bien avec les tableaux
Les informations de type perdu font de la réflexion un gâchis de fonderie et de ruban adhésif.
la source
HashMap
au lieu deHashMap<String, String>
.Je pense que d'autres réponses l'ont dit dans une certaine mesure, mais pas très clairement. L'un des problèmes avec les génériques est la perte des types génériques au cours du processus de réflexion. Donc par exemple:
Malheureusement, les types retournés ne peuvent pas vous dire que les types génériques d'arr sont String. C'est une différence subtile, mais c'est important. Puisque arr est créé à l'exécution, les types génériques sont effacés à l'exécution, de sorte que vous ne pouvez pas vous en assurer. Comme certains l'ont dit, cela
ArrayList<Integer>
ressemble auArrayList<String>
point de vue de la réflexion.Cela n’a peut-être pas d'importance pour l'utilisateur de Generics, mais supposons que nous voulions créer un cadre sophistiqué qui utilise la réflexion pour déterminer de façon compliquée la manière dont l'utilisateur a déclaré les types génériques concrets d'une instance.
Disons que nous voulions qu'une usine générique crée une instance de
MySpecialObject
car c'est le type générique concret que nous avons déclaré pour cette instance. La classe Well Factory ne peut pas s’interroger sur le type concret déclaré pour cette instance car Java les a effacés.Vous pouvez le faire avec les génériques .Net, car au moment de l'exécution, l'objet sait que ce sont des types génériques, car le compilateur l'a intégré au binaire. Avec des effacements, Java ne peut pas faire cela.
la source
Je pourrais dire quelques bonnes choses sur les génériques, mais ce n'était pas la question. Je pourrais me plaindre qu'ils ne sont pas disponibles au moment de l'exécution et ne fonctionnent pas avec les tableaux, mais cela a été mentionné.
Un gros désagrément psychologique: je me trouve parfois dans des situations où les génériques ne peuvent fonctionner. (Les tableaux sont l'exemple le plus simple.) Et je ne peux pas savoir si les génériques ne peuvent pas faire le travail ou si je suis simplement stupide. Je déteste ça. Quelque chose comme les génériques devrait toujours fonctionner. Chaque fois que je ne peux pas faire ce que je veux en utilisant Java-the-language, je sais que le problème est moi, et je sais que si je continue à pousser, je finirai par y arriver. Avec les génériques, si je deviens trop persistant, je peux perdre beaucoup de temps.
Mais le vrai problème est que les génériques ajoutent trop de complexité pour un gain insuffisant. Dans les cas les plus simples, cela peut m'empêcher d'ajouter une pomme à une liste contenant des voitures. Bien. Mais sans génériques, cette erreur déclencherait une exception ClassCastException très rapide au moment de l'exécution, avec une perte de temps minime. Si j'ajoute une voiture avec un siège enfant avec un enfant, ai-je besoin d'un avertissement à la compilation indiquant que la liste ne concerne que les voitures avec des sièges enfants contenant des bébés chimpanzés? Une liste d'instances d'objets simples commence à apparaître comme une bonne idée.
Le code générique peut contenir beaucoup de mots et de caractères, prendre beaucoup d’espace supplémentaire et prendre beaucoup plus de temps à lire. Je peux passer beaucoup de temps à faire fonctionner correctement tout ce code supplémentaire. Quand cela se produit, je me sens incroyablement malin. J'ai également perdu plusieurs heures ou plus de mon temps et je me demande si quelqu'un d'autre sera capable de comprendre le code. J'aimerais pouvoir le confier à des personnes moins intelligentes que moi ou qui ont moins de temps à perdre.
Par contre (je suis un peu fatigué et je ressens le besoin de trouver un peu d’équilibre), c’est bien quand on utilise des collections et des cartes simples d’avoir des ajouts, des mises et des vérifications lorsqu’il est écrit, et cela n’ajoute généralement pas beaucoup. complexité du code ( si quelqu'un d' autre écrit la collection ou la carte) . Et Java est meilleur que C #. Il semble qu'aucune des collections C # que je souhaite utiliser ne gère jamais les génériques. (J'avoue que j'ai des goûts étranges dans les collections.)
la source
Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted.
Cela dépend vraiment du temps d’exécution qui s’écoule entre le démarrage du programme et la frappe de la ligne de code en question. Si un utilisateur signale l'erreur et qu'il vous faut plusieurs minutes pour le reproduire (ou, dans le pire des cas, plusieurs heures ou même plusieurs jours), la vérification au moment de la compilation commence de mieux en mieux ...