Cet article vise à donner une introduction complète au mécanisme de réflexion Java. J'espère que grâce à cet article, vous aurez une compréhension complète du contenu pertinent de la réflexion Java.
Avant de lire cet article, vous pouvez vous référer à " la retenue des génériques Java " .
Préface
Le mécanisme de réflexion Java est une fonction très puissante. Les réflexions peuvent être vues dans de nombreux projets à grande échelle tels que Spring et Mybatis. Grâce au mécanisme de réflexion, nous pouvons obtenir des informations de type d'objet pendant le fonctionnement. En utilisant cette fonctionnalité, nous pouvons implémenter des modèles de conception tels que le mode d'usine et le mode proxy, et peut également résoudre des problèmes pénibles tels que l'effacement générique Java. Dans cet article, nous appliquerons le mécanisme de réflexion Java du point de vue des applications pratiques.
Base de réflexion
PS: Cet article oblige les lecteurs à avoir un certain degré de compréhension de l'API du mécanisme de réflexion. Si vous n'y avez pas été exposé auparavant, il est recommandé d'examiner d'abord le démarrage rapide du document officiel.
Avant d'appliquer le mécanisme de réflexion, voyons d'abord comment obtenir la Class de réflexion correspondant à un objet. En Java, nous avons trois façons d'obtenir la classe de réflexion d'un objet.
Par méthode getClass
Dans Java, chaque Object a une méthode getClass . Grâce à la méthode GetClass, nous pouvons obtenir la classe de réflexion correspondante de cet objet:
String s = "Ziwenxie"; classe <?> C = s.getClass ();
Nous pouvons également appeler la méthode statique forName de Class :
Classe <?> C = class.forname ("java.lang.string"); Ou nous pouvons utiliser .class directement:
Classe <?> C = string.class;
Au début de l'article, nous avons mentionné que l'un des principaux avantages de la réflexion est qu'il nous permet d'obtenir des informations de type d'objet pendant le fonctionnement. Jetons un coup d'œil en détail avec un exemple.
Tout d'abord, nous créons une nouvelle interface A sous typeinfo.interfacea :
package typeInfo.interfacea; interface publique a {void f (); } Ensuite, nous créons une nouvelle interface C sous le package typeinfo.packageaccess . L'interface C hérite de l'interface A , et nous avons également créé plusieurs autres méthodes de test. Notez que les autorisations des méthodes suivantes sont différentes.
package typeInfo.packageAccess; import TypeInfo.interfacea.a; classe C implémente un {public void f () {System.out.println ("public cf ()"); } public void g () {System.out.println ("public cg ()"); } VOID protégé v () {System.out.println ("Cv () protégé"); } void u () {System.out.println ("package cu ()"); } private void w () {System.out.println ("privé cw ()"); }} classe publique Hiddenc {public static a makea () {return new C (); }} Dans callHiddenMethod() , nous utilisons plusieurs nouvelles API, où getDeclaredMethod() est utilisée pour obtenir une méthode que la classe de classe fait référence à l'objet en fonction du nom de la méthode, puis nous pouvons déclencher les méthodes liées de l'objet en appelant invoke() :
Package TypeInfo; Importer TypeInfo.Interfacea.A; Importer TypeInfo.PackageAccess.HidDenc; Importer Java.lang.Reflect.Method; public class HiddenImplementation {public static void main (String [] args) lance une exception {a a = HidDenc.Makea (); af (); System.out.println (a.getClass (). GetName ()); // Oups! La réflexion nous permet toujours d'appeler g (): callhiddenmethod (a, "g"); // et même des méthodes moins accessibles! calhiddenmethod (a, "u"); CallhiddenMethod (a, "v"); Callhiddenmethod (a, "w"); } static void callhiddenMethod (objet A, String MethodName) lève l'exception {méthode g = a.getClass (). getDeclaredMethod (méthodyName); g.setAccessible (true); g.invoke (a); }} D'après les résultats de sortie, nous pouvons voir que s'il s'agit public , default , protect ou pricate , nous pouvons l'appeler librement via la classe de réflexion. Bien sûr, nous devons juste montrer le puissant pouvoir de la réflexion, et cette technique n'est pas recommandée dans le développement réel.
public cf () typeInfo.packageAccess.cpublic cg () package cu () protégé cv () privé cw () privé cw ()
Nous avons le scénario commercial suivant. Nous avons une List<Class<? extends Pet>> . Nous devons compter le nombre Pet spécifiques dans cette classe de collection. En raison de l'effacement générique Java, il n'est certainement pas possible de prêter attention à la pratique similaire à List<? extends Pet> , car une fois que le compilateur a effectué une vérification de type statique, le JVM traitera tous les objets de la collection comme Pet pendant la course, mais il ne saura pas si Pet représente Cat ou Dog , de sorte que les informations de type de l'objet sont réellement perdues pendant la course. PS: À propos de l'effacement générique: J'ai une explication détaillée dans l'article précédent. Les amis intéressés peuvent jeter un œil.
Pour implémenter notre exemple ci-dessus, nous définissons d'abord plusieurs classes:
classe publique PET étend l'individu {public Pet (nom de chaîne) {super (nom); } public Pet () {super (); }} classe publique Cat étend Pet {public cat (String Name) {super (name); } public cat () {super (); }} Classe publique Dog étend PET {public chien (nom de chaîne) {super (nom); }} classe publique EgyptianMau étend Cat {public EgyptianMau (nom de chaîne) {super (nom); } public EgyptianMau () {super (); }} classe publique Mutt étend le chien {public Mutt (nom de chaîne) {super (nom); } public Mutt () {super (); }} Pet ci-dessus hérite de Individual . La mise en œuvre de Individual est un peu plus compliquée. Nous avons implémenté Comparable et redéfini les règles de comparaison des classes. Si nous ne le comprenons pas très bien, cela n'a pas d'importance. Nous l'avons résumé, donc peu importe si nous ne comprenons pas le principe de mise en œuvre.
classe publique Implémentations individuelles comparables <individues> {private statique long compteur = 0; ID long final privé = compteur ++; nom de chaîne privé; // Le nom est un individu public facultatif (nom de chaîne) {this.name = name; } public individuel () {} public String toString () {return getClass (). getImpLename () + (name == null? "": "" + name); } public long id () {return id; } public booléen égaux (objet o) {return o instanceof individu && id == ((individuel) o) .id; } public int hashcode () {int result = 17; if (name! = null) {result = 37 * result + name.hashcode (); } Résultat = 37 * Résultat + (int) id; Résultat de retour; } public int compareto (arg individuel) {// Comparez par nom de classe First: String First = getClass (). getIMPLename (); String argFirst = arg.getClass (). GetImpLename (); int premiercompare = first.compareto (argfirst); if (FirstCompare! = 0) {return firstCompare; } if (name! = null && arg.name! = null) {int secondaryCompare = name.compareto (arg.name); if (secEndCompare! = 0) {return secondaryCompare; }} return (arg.id <id? -1: (arg.id == id? 0: 1)); }} Vous trouverez ci-dessous une classe abstraite PetCreator . À l'avenir, nous pouvons obtenir directement la collection de classes Pet connexes en appelant arrayList() . Ici, nous utilisons la méthode newInstance() que nous n'avons pas mentionnée ci-dessus. Il renverra une instance de la classe à laquelle la classe de classe se réfère vraiment. Qu'est-ce que cela signifie? Par exemple, déclarant new Dog().getClass().newInstance() et Direct new Dog() sont équivalents.
classe abstraite publique PetCreator {private aléatoire rand = new Random (47); // La liste des différents GetTypes de PET à créer: Liste abstraite publique <classe <? étend TEP >> getTypes (); public Pet Randompet () {// Créer un animal aléatoire int n = rand.nextint (getTypes (). size ()); essayez {return getTypes (). get (n) .newInstance (); } catch (InstantiationException e) {Throw New RuntimeException (e); } catch (illégalaccessException e) {lancer une nouvelle RuntimeException (e); }} public Pet [] createArray (int size) {pet [] result = new Pet [size]; for (int i = 0; i <size; i ++) {result [i] = randompet (); } Retour Résultat; } public ArrayList <et> ArrayList (int size) {ArrayList <At> Result = new ArrayList <At> (); Collection.Addall (résultat, CreatArray (taille)); Résultat de retour; }} Ensuite, implémentons la classe abstraite ci-dessus et expliquons le code suivant. Dans le code suivant, nous déclarons deux classes de collecte, allTypes et types , parmi lesquelles allTypes contient toutes les classes déclarées ci-dessus, mais nos types spécifiques ne sont en fait que deux types, à savoir Mutt et EgypianMau , donc l'animal dont nous avons vraiment besoin pour obtenir new sont les types contenus dans types . À l'avenir, nous pouvons obtenir les types contenus dans types en appelant getTypes() .
La classe publique LitteralPetCreator étend PetCreator {@SuppressWarnings ("Un d'échecs") Liste finale statique publique <class <? étend le pet >> allTypes = collections.unmodifiabeblelist (arrays.aslist (pet.class, dog.class, cat.class, mutt.class, Egyptianmau.class)); Liste finale statique privée <classe <? étend TEP >> Types = AllTypes.Sublist (AllTypes.Indexof (Mutt.Class), AllTypes.Size ()); Liste publique <classe <? étend TEP >> getTypes () {return types; }} La logique globale est terminée, et enfin nous implémentons TypeCounter utilisée pour compter le nombre de classes Pet pertinentes dans l'ensemble. Expliquez isAssignalbeFrom() , qui peut déterminer qu'une classe de réflexion est une sous-classe sous-classe ou indirecte d'une classe de réflexion. Comme son nom l'indique, getSuperclass() est d'obtenir la classe parent d'une classe de réflexion.
La classe publique TypeCounter étend Hashmap <class <?>, Integer> {private class <?> Basetype; public TypeCounter (class <?> Basetype) {this.basetype = basetype; } public void count (objet obj) {class <?> type = obj.getClass (); if (! BasEtype.isAssignableFrom (type)) {Throw New RuntimeException (obj + "Type incorrect" + type + ", devrait être le type ou le sous-type de" + Basetype); } countClass (type); } private void countclass (class <?> type) {Integer Quantity = get (type); put (type, quantité == null? 1: quantité + 1); Class <?> Superclass = type.getsuperclass (); if (superclass! = null && Basetype.isAssignableFrom (superclass)) {countclass (superclass); }} @Override public String toString () {stringBuilder result = new StringBuilder ("{"); for (map.entry <class <?>, entier> paire: entrySet ()) {result.append (pair.getKey (). getImPlename ()); result.append ("="); result.append (pair.getValue ()); Result.APPEND (","); } result.delete (result.length () - 2, result.Length ()); result.append ("}"); return result.toString (); }}Résumer
Ce qui précède est tout le contenu de cet article sur l'exemple de partage de code du mécanisme de réflexion Java, et j'espère que cela sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à ce site:
Code d'implémentation de la programmation Java Programmation et de l'impression
Explication détaillée de la mise en œuvre des références et de la procuration dynamique en Java
Programmation Java pour implémenter le partage de code simple de l'éclipse lunaire
S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien pour ce site!