Java génériques type d'effacement: quand et ce qui se passe?
J'ai lu sur Java de type de l'effacement sur le site web d'Oracle.
Quand le type d'effacement de se produire? Au moment de la compilation ou de l'exécution? Lorsque la classe est chargée? Lorsque la classe est instanciée?
Beaucoup de sites (y compris le tutoriel officiel mentionné ci-dessus) dire le type d'effacement se produit au moment de la compilation. Si le type d'informations est totalement supprimé au moment de la compilation, comment le JDK vérifier la compatibilité du type quand une méthode utilisant des génériques est invoquée sans informations de type ou de type incorrect de l'information?
Considérons l'exemple suivant: supposons que la classe A
a une méthode, empty(Box<? extends Number> b)
. Nous compilons A.java
et d'obtenir le fichier de classe A.class
.
public class A {
public static void empty(Box<? extends Number> b) {}
}
public class Box<T> {}
Maintenant nous allons créer une autre classe B
qui invoque la méthode empty
avec un non paramétrée argument (raw): empty(new Box())
. Si nous compilons B.java
avec A.class
dans le classpath, javac est assez intelligent pour élever un avertissement. Donc A.class
a un certain type d'informations qui y sont stockées.
public class B {
public static void invoke() {
//java: unchecked method invocation:
// method empty in class A is applied to given types
// required: Box<? extends java.lang.Number>
// found: Box
//java: unchecked conversion
// required: Box<? extends java.lang.Number>
// found: Box
A.empty(new Box());
}
}
J'imagine que le type d'effacement se produit lorsque la classe est chargée, mais c'est juste une supposition. Alors quand est-ce arrivé?
OriginalL'auteur | 2008-12-04
Vous devez vous connecter pour publier un commentaire.
Type d'effacement s'applique à la utilisation des génériques. Il y a certainement des métadonnées dans le fichier de classe pour dire si oui ou non une méthode/type est générique, et que les contraintes sont etc. Mais quand les génériques sont utilisé, ils sont convertis en des contrôles de compilation et de l'exécution à temps des moulages. Donc ce code:
est compilé dans
Au moment de l'exécution il n'y a aucun moyen de savoir que
T=String
pour la liste de l'objet que l'information est allé.... mais le
List<T>
interface est lui-même se présente comme étant générique.EDIT: Juste pour préciser, le compilateur ne conserve les informations sur la variable être un
List<String>
- mais vous ne pouvez toujours pas trouver queT=String
pour la liste de l'objet lui-même.Comme j'ai répondu à votre commentaire, je crois que vous êtes de se confondre entre le fait d'être en mesure d'obtenir le type d'une variable et être en mesure d'obtenir le type d'une . L'objet lui-même ne connaît pas son type d'argument, même si le champ n'.
Bien sûr, simplement en regardant l'objet lui-même, vous ne pouvez pas savoir que c'est une Liste<String>. Mais les objets ne sont pas simplement apparaître de nulle part. Ils sont créés localement, transmis sous la forme d'une invocation de méthode argument, retourné comme valeur de retour d'un appel de méthode, ou de lire à partir d'un champ d'un objet... Dans tous ces cas, vous POUVEZ savoir à l'exécution de ce que le type générique est, implicitement ou à l'aide de la Réflexion Java API.
Comment savez-vous où l'objet est venu de si? Si vous avez un paramètre de type
List<? extends InputStream>
comment pouvez-vous savoir quel type il était quand il a été créé? Même si vous trouver le type de champ à la référence a été stockée dans, pourquoi devriez-vous? Pourquoi devriez-vous être en mesure d'obtenir tout le reste de l'information à propos de l'objet au moment de l'exécution, mais pas son générique type d'arguments? Vous semblez être en essayant de faire le type d'effacement être cette petite chose qui n'affecte pas les développeurs ont vraiment que je trouve que c'est une très important problème dans certains cas.Mais le type d'effacement EST une toute petite chose, qui n'a pas vraiment d'incidence sur les développeurs! Bien sûr, je ne peux pas parler pour les autres, mais dans MON expérience, il n'a jamais été une grosse affaire. J'ai réellement profiter de runtime type d'information dans la conception de mon Java se moquant de l'API (JMockit); paradoxalement, .NET moqueur Api semblent prendre moins l'avantage de le générique du type de système en C#.
OriginalL'auteur Jon Skeet
Le compilateur est responsable de comprendre les Génériques au moment de la compilation. Le compilateur est également responsable de jeter cette "compréhension" des classes génériques, dans un processus que nous appelons type d'effacement. Tout se passe au moment de la compilation.
Remarque: Contrairement aux croyances de la majorité des développeurs Java, il est possible de conserver le type de compilation de l'information et de récupérer cette information au moment de l'exécution, malgré un très limitées. En d'autres termes: Java fournit réifiée génériques en très peu de.
Sur le type d'effacement
Avis que, au moment de la compilation, le compilateur en a plein le type d'informations disponibles, mais cette information est volontairement abandonné en général lorsque le byte-code est généré, dans un processus connu sous le nom type d'effacement. C'est fait de cette façon en raison de problèmes de compatibilité: L'intention de la langue concepteurs a été de fournir le code source complet de la compatibilité et de plein octet code de la compatibilité entre les versions de la plate-forme. S'il était mis en œuvre différemment, vous devez recompiler vos applications héritées lors de la migration vers les nouvelles versions de la plate-forme. La façon dont c'était fait, toutes les signatures de méthode sont conservés (la compatibilité du code source) et que vous n'avez pas besoin de recompiler quoi que ce soit (compatibilité binaire).
Concernant réifiée génériques en Java
Si vous avez besoin de garder le type de compilation de l'information, vous avez besoin d'employer anonyme classes.
Le point est: dans le cas très particulier de anonyme des classes, il est possible de récupérer le plein au moment de la compilation des informations de type à l'exécution qui, en d'autres termes signifie: réifiée génériques. Cela signifie que le compilateur ne pas jeter les informations de type lorsque les classes anonymes sont impliqués; cette information est conservée dans le code binaire généré et le système d'exécution vous permet de récupérer cette information.
J'ai écrit un article sur ce sujet:
http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html
Une remarque à propos de la technique décrite dans l'article ci-dessus est que la technique est obscur pour la majorité des développeurs. Malgré cela fonctionne et fonctionne bien, la plupart des développeurs se sentir confus ou mal à l'aise avec la technique. Si vous avez un code partagé de base ou d'un plan pour libérer votre code pour le public, je ne recommande pas la technique ci-dessus. D'autre part, si vous êtes l'unique utilisateur de votre code, vous pouvez profiter de la puissance de cette technique offre à vous.
Exemple de code
L'article ci-dessus a des liens vers des exemples de code.
J'ai amélioré la réponse et j'ai ajouté un lien vers quelques cas de test. Cheers 🙂
En fait, ils n'ont pas de maintenir à la fois binaire et la compatibilité source: oracle.com/technetwork/java/javase/compatibility-137462.html Où puis-je en savoir plus sur leur intention? Docs dire qu'il utilise le type d'effacement, mais ne dit pas pourquoi.
En effet, excellent article! Vous pourriez ajouter que les classes locales trop de travail et que, dans les deux cas (anonyme et classes locales), des informations sur le type d'argument n'est conservée que dans le cas d'un accès direct
new Box<String>() {};
pas dans le cas d'un accès indirectvoid foo(T) {...new Box<T>() {};...}
, car le compilateur ne conserve pas les informations de type pour l'affichage de déclaration de la méthode.C'est très cool. Plus de gens devraient savoir ce
OriginalL'auteur Richard Gomes
Si vous avez un champ qui est un type générique, son type de paramètres sont compilés dans la classe.
Si vous avez une méthode qui prend ou retourne un type générique, ces paramètres de type sont compilés dans la classe.
Cette information est ce que le compilateur utilise pour vous dire que vous ne pouvez pas passer un
Box<String>
à laempty(Box<T extends Number>)
méthode.L'API est compliqué, mais vous pouvez inspecter ce type d'informations par le biais de l'API reflection avec des méthodes comme
getGenericParameterTypes
,getGenericReturnType
, et, pour les champs,getGenericType
.Si vous avez un code qui utilise un type générique, le compilateur insère jette comme nécessaire (l'appelant) pour vérifier les types. Le générique des objets eux-mêmes sont seulement la première de type; le paramétrée type est "effacée". Ainsi, lorsque vous créez un
new Box<Integer>()
, il n'y a pas d'information concernant lesInteger
classe dans leBox
objet.Angelika Langer de la FAQ est la meilleure référence que j'ai vu pour Java Génériques.
OriginalL'auteur erickson
Les médicaments génériques en Langage Java est un très bon guide sur ce sujet.
Donc, c'est au moment de la compilation. La JVM ne saura jamais qui
ArrayList
vous avez utilisé.Je recommande aussi M. Skeet sa réponse sur Quel est le concept de l'effacement dans les génériques en Java?
OriginalL'auteur Eugene Yokota
Type d'effacement se produit au moment de la compilation. Ce type d'effacement signifie, c'est qu'il fera oublier le type générique, pas à propos de chaque type. En outre, il y aura toujours des métadonnées sur les types génériques. Par exemple
est converti à
au moment de la compilation. Vous pouvez obtenir des avertissements non pas parce que le compilateur sait sur quel type est le générique d', mais au contraire, parce qu'il ne sait pas assez donc il ne peut pas garantir la sécurité de type.
En outre, le compilateur ne conserver que le type d'informations sur les paramètres d'un appel de méthode, que vous pouvez récupérer par la réflexion.
Ce guide est le meilleur que j'ai trouvé sur le sujet.
OriginalL'auteur Vinko Vrsalovic
Le terme de type "effacement" n'est pas vraiment à la description correcte de Java problème avec les génériques.
Type d'effacement n'est pas en soi une mauvaise chose, en effet il est très nécessaire pour la performance et est souvent utilisé dans plusieurs langages tels que C++, Haskell, D.
Avant de dégoût, veuillez vous rappeler de la définition de type d'effacement de Wiki
Quel est le type d'effacement?
Type d'effacement moyen de jeter les balises de type créé au moment de la conception ou déduit balises de type au moment de la compilation, tels que le programme compilé en code binaire ne contient pas de n'importe quel type de balises.
Et c'est le cas pour chaque langage de programmation de la compilation en code binaire, sauf dans certains cas où vous avez besoin d'exécution des balises. Ces exceptions comprennent par exemple tous les existentielle types (Java les Types de Référence qui sont subtypeable, n'Importe quel Type dans de nombreuses langues, Types d'Union).
La raison pour le type d'effacement est que les programmes se transforment en une langue qui est en quelque sorte uni-typés (langage binaire permettant seulement de bits) que les types sont des abstractions seulement et d'affirmer une structure pour ses valeurs et la sémantique pour les gérer.
C'est donc en retour, une normale chose de naturel.
De Java problème est différent et a causé à la façon dont elle réifie.
Souvent fait des déclarations à propos de Java n'a pas réifiée des génériques est également faux.
Java ne réification, mais dans un mauvais chemin des raisons de compatibilité descendante.
Ce qui est de la réification?
De notre Wiki
Réification moyen de convertir quelque chose d'abstrait (Paramétrique de Type) en quelque chose de béton (Type) par spécialisation.
Nous illustrons cela par un exemple simple:
Une liste de tableaux avec définition:
est une abstraction, dans le détail d'un constructeur de type, qui est "réifiée" quand spécialisée avec un type concret, disons Entier:
où
ArrayList<Integer>
est vraiment un type.Mais c'est exactement la chose que Java ne marche pas!!!, au lieu de cela ils réification constamment résumé des types avec leurs limites, c'est à dire produisant le même type de béton indépendant des paramètres passés dans la spécialisation:
qui est ici réifiée avec l'implicite de l'Objet lié (
ArrayList<T extends Object>
==ArrayList<T>
).Malgré qu'il rend le générique tableaux inutilisable et la cause de quelques erreurs étranges pour les matières premières, les types:
il provoque beaucoup d'ambiguïtés
reportez-vous à la même fonction:
et, par conséquent, générique surcharge de la méthode ne peut pas être utilisé en Java.
OriginalL'auteur iconfly
Que j'ai rencontré avec le type d'effacement dans Android. Dans la production, nous utilisons gradle avec rapetisser option. Après minification j'ai exception irrécupérable. J'ai fait simple fonction pour afficher la chaîne d'héritage de mon objet:
Et il ya deux résultats de cette fonction:
Pas minimisé code:
Minifiés code:
Donc, dans minifiés code réel paramétrées, les classes sont remplacés par les premières classes de types sans aucun type d'information.
Comme une solution pour mon projet, j'ai enlevé toutes les réflexions des appels et replced avec explicite params types passés en arguments de la fonction.
OriginalL'auteur porfirion