Cela est apparu comme une question que j'ai posée lors d'une interview récemment comme quelque chose que le candidat souhaitait voir ajouté au langage Java. Il est communément identifié comme une douleur que Java n'ait pas de génériques réifiés mais, une fois poussé, le candidat ne pouvait pas vraiment me dire le genre de choses qu'il aurait pu accomplir s'ils étaient là.
Évidemment, comme les types bruts sont autorisés en Java (et les vérifications non sécurisées), il est possible de subvertir les génériques et de se retrouver avec un List<Integer>
qui (par exemple) contient en fait String
s. Cela pourrait clairement être rendu impossible si les informations de type étaient réifiées; mais il doit y avoir plus que ça !
Les gens pourraient-ils publier des exemples de choses qu'ils voudraient vraiment faire , si des génériques réifiés étaient disponibles? Je veux dire, évidemment, vous pouvez obtenir le type de a List
au moment de l'exécution - mais que feriez-vous avec?
public <T> void foo(List<T> l) {
if (l.getGenericType() == Integer.class) {
//yeah baby! err, what now?
EDIT : Une mise à jour rapide à cela car les réponses semblent principalement préoccupées par la nécessité de passer un Class
comme paramètre (par exemple EnumSet.noneOf(TimeUnit.class)
). Je cherchais davantage quelque chose du genre où ce n'est tout simplement pas possible . Par exemple:
List<?> l1 = api.gimmeAList();
List<?> l2 = api.gimmeAnotherList();
if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
l1.addAll(l2); //why on earth would I be doing this anyway?
la source
findClass()
faudrait ignorer le paramétrage, maisdefineClass()
ne le pourrait pas). Et comme nous le savons, les pouvoirs en place sont primordiaux pour la rétrocompatibilité.Réponses:
Depuis les quelques fois où je suis tombé sur ce "besoin", cela se résume finalement à cette construction:
Cela fonctionne en C # en supposant qu'il
T
a un constructeur par défaut . Vous pouvez même obtenir le type d'exécutiontypeof(T)
et obtenir les constructeursType.GetConstructor()
.La solution Java courante serait de passer l'
Class<T>
argument as.(il n'a pas nécessairement besoin d'être passé en tant qu'argument constructeur, car un argument de méthode convient également, ce qui précède n'est qu'un exemple, et le
try-catch
est également omis par souci de concision)Pour toutes les autres constructions de type générique, le type réel peut facilement être résolu avec un peu d'aide de réflexion. Les questions-réponses ci-dessous illustrent les cas d'utilisation et les possibilités:
la source
T.class
ouT.getClass()
, de sorte que vous puissiez accéder à tous ses champs, constructeurs et méthodes. Cela rend également la construction impossible.La chose qui me pique le plus souvent est l'incapacité de tirer parti de l'envoi multiple sur plusieurs types génériques. Ce qui suit n'est pas possible et dans de nombreux cas, ce serait la meilleure solution:
la source
public void my_method(List input) {}
. Je n'ai cependant jamais rencontré ce besoin, simplement parce qu'ils n'auraient pas le même nom. S'ils ont le même nom, je me demande si cepublic <T extends Object> void my_method(List<T> input) {}
n'est pas une meilleure idée.myStringsMethod(List<String> input)
etmyIntegersMethod(List<Integer> input)
même si une surcharge pour un tel cas était possible en Java.<algorithm>
en C ++.La sécurité des types vient à l'esprit. Le downcasting vers un type paramétré sera toujours dangereux sans les génériques réifiés:
De plus, les abstractions fuiraient moins - du moins celles qui pourraient être intéressées par des informations d'exécution sur leurs paramètres de type. Aujourd'hui, si vous avez besoin d'informations d'exécution sur le type de l'un des paramètres génériques, vous devez également les transmettre
Class
. De cette façon, votre interface externe dépend de votre implémentation (que vous utilisiez ou non RTTI pour vos paramètres).la source
ParametrizedList
qui copie les données dans les types de vérification de la collection source. C'est un peu commeCollections.checkedList
mais peut être semé avec une collection pour commencer.T.class.getAnnotation(MyAnnotation.class)
(oùT
est un type générique) sans changer l'interface externe.Vous pourriez créer des tableaux génériques dans votre code.
la source
String[] strings = new String[1]; Object[] objects = strings; objects[0] = new Object();
Compile bien dans les deux langues. Ne fonctionne pas très bien.C'est une vieille question, il y a une tonne de réponses, mais je pense que les réponses existantes sont hors de propos.
«réifié» signifie simplement réel et signifie généralement le contraire de l'effacement de type.
Le gros problème lié aux génériques Java:
void method(List<A> l)
etmethod(List<B> l)
. Cela est dû à l'effacement de caractères mais est extrêmement mesquin.la source
deserialize(thingy, List<Integer>.class)
La sérialisation serait plus simple avec la réification. Ce que nous voudrions, c'est
Ce que nous devons faire c'est
semble moche et fonctionne bizarrement.
Il y a aussi des cas où il serait vraiment utile de dire quelque chose comme
Ces choses ne mordent pas souvent, mais elles mordent quand elles se produisent.
la source
Mon exposition à Java Geneircs est assez limitée, et à part les points que d'autres réponses ont déjà mentionnés, il y a un scénario expliqué dans le livre Java Generics and Collections , par Maurice Naftalin et Philip Walder, où les génériques réifiés sont utiles.
Étant donné que les types ne sont pas réifiables, il n'est pas possible d'avoir des exceptions paramétrées.
Par exemple, la déclaration du formulaire ci-dessous n'est pas valide.
En effet, la clause catch vérifie si l'exception levée correspond à un type donné. Cette vérification est identique à la vérification effectuée par le test d'instance et comme le type n'est pas réifiable, la forme d'instruction ci-dessus n'est pas valide.
Si le code ci-dessus était valide, la gestion des exceptions de la manière ci-dessous aurait été possible:
Le livre mentionne également que si les génériques Java sont définis de la même manière que les modèles C ++ (expansion), cela peut conduire à une implémentation plus efficace car cela offre plus de possibilités d'optimisation. Mais n'offre aucune explication plus que cela, donc toute explication (pointeurs) de la part de personnes bien informées serait utile.
la source
Les tableaux joueraient probablement beaucoup mieux avec les génériques s'ils étaient réifiés.
la source
List<String>
n'est pas unList<Object>
.J'ai un wrapper qui présente un jeu de résultats jdbc comme un itérateur, (cela signifie que je peux tester des opérations basées sur une base de données beaucoup plus facilement grâce à l'injection de dépendances).
L'API ressemble à
Iterator<T>
où T est un type qui peut être construit en utilisant uniquement des chaînes dans le constructeur. L'itérateur examine ensuite les chaînes renvoyées par la requête SQL, puis essaie de les faire correspondre à un constructeur de type T.Dans la manière actuelle d'implémentation des génériques, je dois également passer dans la classe des objets que je vais créer à partir de mon jeu de résultats. Si je comprends bien, si les génériques étaient réifiés, je pourrais simplement appeler T.getClass () pour obtenir ses constructeurs, et ensuite ne pas avoir à convertir le résultat de Class.newInstance (), ce qui serait beaucoup plus soigné.
Fondamentalement, je pense que cela facilite l'écriture d'API (par opposition à la simple écriture d'une application), car vous pouvez déduire beaucoup plus d'objets, et donc moins de configuration sera nécessaire ... Je n'ai pas apprécié les implications des annotations jusqu'à ce que je les ai vus être utilisés dans des choses comme spring ou xstream au lieu de rames de configuration.
la source
Une bonne chose serait d'éviter la boxe pour les types primitifs (valeur). Ceci est en quelque sorte lié à la plainte de tableau que d'autres ont soulevée, et dans les cas où l'utilisation de la mémoire est limitée, cela pourrait en fait faire une différence significative.
Il existe également plusieurs types de problèmes lors de l'écriture d'un framework où il est important de pouvoir réfléchir sur le type paramétré. Bien sûr, cela peut être contourné en passant un objet de classe au moment de l'exécution, mais cela obscurcit l'API et impose une charge supplémentaire à l'utilisateur du framework.
la source
new T[]
là où T est de type primitif!Ce n'est pas que vous obtiendrez quelque chose d'extraordinaire. Ce sera simplement plus simple à comprendre. L'effacement de type semble être une période difficile pour les débutants, et cela nécessite finalement une compréhension du fonctionnement du compilateur.
Mon opinion est que les génériques sont simplement un extra qui évite beaucoup de casting redondant.
la source
Quelque chose que toutes les réponses ici ont manqué et qui est constamment un casse-tête pour moi est que, puisque les types sont effacés, vous ne pouvez pas hériter d'une interface générique deux fois. Cela peut poser problème lorsque vous souhaitez créer des interfaces à granularité fine.
la source
En voici une qui m'a attrapé aujourd'hui: sans réification, si vous écrivez une méthode qui accepte une liste varargs d'éléments génériques ... les appelants peuvent PENSER qu'ils sont typés, mais passer accidentellement dans n'importe quel vieux crud, et faire exploser votre méthode.
Cela semble peu probable? ... Bien sûr, jusqu'à ce que ... vous utilisiez Class comme type de données. À ce stade, votre appelant vous enverra volontiers de nombreux objets de classe, mais une simple faute de frappe vous enverra des objets de classe qui n'adhèrent pas à T, et un désastre se produit.
(NB: j'ai peut-être fait une erreur ici, mais en cherchant sur Google "generics varargs", ce qui précède semble être exactement ce à quoi vous vous attendez. Ce qui en fait un problème pratique, c'est l'utilisation de Class, je pense - les appelants semblent pour être moins prudent :()
Par exemple, j'utilise un paradigme qui utilise des objets de classe comme clé dans les cartes (c'est plus complexe qu'une simple carte - mais conceptuellement, c'est ce qui se passe).
par exemple, cela fonctionne très bien dans Java Generics (exemple trivial):
par exemple sans réification en Java Generics, celui-ci accepte TOUT objet "Class". Et ce n'est qu'une toute petite extension du code précédent:
Les méthodes ci-dessus doivent être écrites des milliers de fois dans un projet individuel - de sorte que le risque d'erreur humaine devient élevé. Les erreurs de débogage se révèlent «pas amusantes». J'essaie actuellement de trouver une alternative, mais je n'ai pas beaucoup d'espoir.
la source