Question Qu'est-ce que la réflexion et pourquoi est-ce utile?


Qu'est-ce que la réflexion, et pourquoi est-ce utile?

Je suis particulièrement intéressé par Java, mais je suppose que les principes sont les mêmes dans toutes les langues.


1690
2017-09-01 08:39


origine


Réponses:


La réflexion de nom est utilisée pour décrire le code qui est capable d'inspecter d'autres codes dans le même système (ou lui-même).

Par exemple, supposons que vous ayez un objet de type inconnu en Java et que vous souhaitiez appeler une méthode "doSomething" s'il existe. Le système de typage statique de Java n'est pas vraiment conçu pour supporter ceci à moins que l'objet ne se conforme à une interface connue, mais en utilisant la réflexion, votre code peut regarder l'objet et savoir s'il a une méthode appelée "faireQuelque chose" vouloir.

Donc, pour vous donner un exemple de code en Java (imaginez l'objet en question est foo):

Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);

Un cas d'utilisation très commun en Java est l'utilisation avec des annotations. JUnit 4, par exemple, utilisera la réflexion pour regarder dans vos classes les méthodes marquées avec l'annotation @Test, et les appellera ensuite lors de l'exécution du test unitaire.

Il y a quelques bons exemples de réflexion pour vous aider à démarrer http://docs.oracle.com/javase/tutorial/reflect/index.html

Et enfin, oui, les concepts sont assez similaires dans d'autres langages de types statiques qui supportent la réflexion (comme C #). Dans les langages typés dynamiquement, le cas d'utilisation décrit ci-dessus est moins nécessaire (puisque le compilateur permet d'appeler n'importe quelle méthode, échouant à l'exécution s'il n'existe pas), mais le second cas de recherche de méthodes marquées ou travailler d'une certaine manière est encore commun.

Mise à jour à partir d'un commentaire:

La possibilité d'inspecter le code dans le système et de voir les types d'objets est   pas de réflexion, mais plutôt de type Introspection. La réflexion est alors la   capacité à faire des modifications à l'exécution en utilisant des   introspection. La distinction est nécessaire ici comme certaines langues   soutenir l'introspection, mais ne supporte pas la réflexion. Un exemple   est C ++


1414
2017-09-01 08:44



Réflexion est la capacité d'un langage à inspecter et à appeler dynamiquement des classes, des méthodes, des attributs, etc. lors de l'exécution.

Par exemple, tous les objets dans Java ont la méthode getClass(), qui vous permet de déterminer la classe de l'objet même si vous ne le connaissez pas au moment de la compilation (par exemple si vous l'avez déclaré comme Object) - cela peut sembler trivial, mais une telle réflexion n'est pas possible dans des langues moins dynamiques telles que C++. Des utilisations plus avancées vous permettent de lister et d'appeler des méthodes, des constructeurs, etc.

La réflexion est importante car elle vous permet d'écrire des programmes qui ne doivent pas "tout" savoir à la compilation, ce qui les rend plus dynamiques, puisqu'ils peuvent être liés à l'exécution. Le code peut être écrit contre des interfaces connues, mais les classes réelles à utiliser peuvent être instanciées en utilisant la réflexion à partir des fichiers de configuration.

Beaucoup de cadres modernes utilisent largement la réflexion pour cette raison. La plupart des autres langages modernes utilisent également la réflexion, et dans les langages de script (tels que Python), ils sont encore plus étroitement intégrés, car ils se sentent plus naturels dans le modèle de programmation générale de ces langages.


195
2017-09-01 08:52



L'une de mes utilisations préférées de la réflexion est la méthode de vidage Java ci-dessous. Il prend n'importe quel objet en paramètre et utilise l'API de réflexion Java pour imprimer chaque nom et valeur de champ.

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public static String dump(Object o, int callCount) {
    callCount++;
    StringBuffer tabs = new StringBuffer();
    for (int k = 0; k < callCount; k++) {
        tabs.append("\t");
    }
    StringBuffer buffer = new StringBuffer();
    Class oClass = o.getClass();
    if (oClass.isArray()) {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("[");
        for (int i = 0; i < Array.getLength(o); i++) {
            if (i < 0)
                buffer.append(",");
            Object value = Array.get(o, i);
            if (value.getClass().isPrimitive() ||
                    value.getClass() == java.lang.Long.class ||
                    value.getClass() == java.lang.String.class ||
                    value.getClass() == java.lang.Integer.class ||
                    value.getClass() == java.lang.Boolean.class
                    ) {
                buffer.append(value);
            } else {
                buffer.append(dump(value, callCount));
            }
        }
        buffer.append(tabs.toString());
        buffer.append("]\n");
    } else {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("{\n");
        while (oClass != null) {
            Field[] fields = oClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                buffer.append(tabs.toString());
                fields[i].setAccessible(true);
                buffer.append(fields[i].getName());
                buffer.append("=");
                try {
                    Object value = fields[i].get(o);
                    if (value != null) {
                        if (value.getClass().isPrimitive() ||
                                value.getClass() == java.lang.Long.class ||
                                value.getClass() == java.lang.String.class ||
                                value.getClass() == java.lang.Integer.class ||
                                value.getClass() == java.lang.Boolean.class
                                ) {
                            buffer.append(value);
                        } else {
                            buffer.append(dump(value, callCount));
                        }
                    }
                } catch (IllegalAccessException e) {
                    buffer.append(e.getMessage());
                }
                buffer.append("\n");
            }
            oClass = oClass.getSuperclass();
        }
        buffer.append(tabs.toString());
        buffer.append("}\n");
    }
    return buffer.toString();
}

84
2017-09-02 16:15



Utilisations de la réflexion

La réflexion est couramment utilisée par les programmes qui requièrent la possibilité d'examiner ou de modifier le comportement d'exécution des applications s'exécutant dans la machine virtuelle Java. C'est une fonctionnalité relativement avancée et ne devrait être utilisée que par des développeurs qui maîtrisent les bases de la langue. Avec cette réserve à l'esprit, la réflexion est une technique puissante et peut permettre aux applications d'effectuer des opérations qui seraient autrement impossibles.

Fonctions d'extensibilité

Une application peut utiliser des classes externes définies par l'utilisateur en créant des instances d'objets d'extensibilité en utilisant leurs noms pleinement qualifiés. Navigateurs de classe et environnements de développement visuel Un navigateur de classes doit pouvoir énumérer les membres des classes. Les environnements de développement visuel peuvent bénéficier de l'utilisation d'informations de type disponibles en réflexion pour aider le développeur à écrire du code correct. Débogueurs et outils de test Les débogueurs doivent pouvoir examiner les membres privés dans les classes. Les harnais de test peuvent utiliser la réflexion pour appeler systématiquement une API de jeu détectable définie sur une classe, afin d'assurer un niveau élevé de couverture de code dans une suite de tests.

Inconvénients de la réflexion

La réflexion est puissante, mais ne doit pas être utilisée sans discernement. S'il est possible d'effectuer une opération sans utiliser de réflexion, il est préférable d'éviter de l'utiliser. Les préoccupations suivantes doivent être gardées à l'esprit lors de l'accès au code par réflexion.

  • Frais généraux de performance

Parce que la réflexion implique des types qui sont résolus dynamiquement, certaines optimisations de machines virtuelles Java ne peuvent pas être effectuées. Par conséquent, les opérations réfléchissantes ont des performances plus lentes que leurs homologues non réfléchissantes et doivent être évitées dans les sections de code fréquemment appelées applications sensibles aux performances.

  • Restrictions de sécurité

La réflexion nécessite une autorisation d'exécution qui peut ne pas être présente lors de l'exécution sous un gestionnaire de sécurité. C'est une considération importante pour le code qui doit s'exécuter dans un contexte de sécurité restreint, comme dans une applet.

  • Exposition des Internes

Puisque la réflexion permet au code d'effectuer des opérations qui seraient illégales dans un code non-réfléchissant, comme l'accès à des champs et méthodes privés, l'utilisation de la réflexion peut entraîner des effets secondaires inattendus qui peuvent rendre le code dysfonctionnel et détruire la portabilité. Le code réfléchi brise les abstractions et peut donc changer le comportement avec les mises à niveau de la plate-forme.

la source: L'API Reflection


55
2017-10-17 11:59



La réflexion est un mécanisme clé pour permettre à une application ou un framework de fonctionner avec du code qui n'a peut-être même pas encore été écrit!

Prenez par exemple votre fichier web.xml typique. Cela contiendra une liste d'éléments de servlet, qui contiennent des éléments de classe servlet imbriqués. Le conteneur de servlet traite le fichier web.xml et crée une nouvelle instance de chaque classe de servlet par réflexion.

Un autre exemple serait l'API Java pour le traitement XML (JAXP). Où un fournisseur d'analyseur XML est «branché» via des propriétés système bien connues, qui sont utilisées pour construire de nouvelles instances par réflexion.

Et enfin, l'exemple le plus complet est Printemps qui utilise la réflexion pour créer ses haricots, et pour son utilisation intensive des proxies


30
2017-09-01 09:30



Toutes les langues ne supportent pas la réflexion, mais les principes sont généralement les mêmes dans les langues qui la supportent.

La réflexion est la capacité de «réfléchir» sur la structure de votre programme. Ou plus concret. Pour regarder les objets et les classes que vous avez et récupérer par programme des informations sur les méthodes, les champs et les interfaces qu'ils implémentent. Vous pouvez également regarder des choses comme des annotations.

C'est utile dans beaucoup de situations. Partout où vous voulez être en mesure de brancher dynamiquement des classes dans votre code. Beaucoup de mappeurs relationnels d'objets utilisent la réflexion pour être capables d'instancier des objets à partir de bases de données sans savoir à l'avance quels objets ils vont utiliser. Les architectures plug-in sont un autre endroit où la réflexion est utile. Pouvoir charger dynamiquement du code et déterminer s'il existe des types implémentant la bonne interface à utiliser en tant que plugin est important dans ces situations.


27
2017-09-01 08:50



La réflexion permet l'instanciation de nouveaux objets, l'invocation de méthodes et l'obtention / la définition dynamique d'opérations sur des variables de classe au moment de l'exécution, sans avoir préalablement connaissance de son implémentation.

Class myObjectClass = MyObject.class;
Method[] method = myObjectClass.getMethods();

//Here the method takes a string parameter if there is no param, put null.
Method method = aClass.getMethod("method_name", String.class); 

Object returnValue = method.invoke(null, "parameter-value1");

Dans l'exemple ci-dessus, le paramètre null est l'objet sur lequel vous voulez appeler la méthode. Si la méthode est statique, vous indiquez null. Si la méthode n'est pas statique, vous devez fournir une instance MyObject valide au lieu de null lors de l'appel.

La réflexion vous permet également d'accéder aux membres privés / méthodes d'une classe:

public class A{

  private String str= null;

  public A(String str) {
  this.str= str;
  }
}

.

A obj= new A("Some value");

Field privateStringField = A.class.getDeclaredField("privateString");

//Turn off access check for this field
privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(obj);
System.out.println("fieldValue = " + fieldValue);
  • Pour l'inspection des classes (également appelées introspection), vous n'avez pas besoin d'importer le paquet de réflexion (java.lang.reflect). Les métadonnées de classe peuvent être consultées via java.lang.Class.

Reflection est une API très puissante mais elle peut ralentir l'application si elle est utilisée en excès, car elle résout tous les types lors de l'exécution.


26
2017-07-08 16:12



Exemple :
Prenons par exemple une application distante qui donne à votre application un objet que vous obtenez en utilisant ses méthodes API. Maintenant, basé sur l'objet, vous devrez peut-être effectuer une sorte de calcul.
Le fournisseur garantit que l'objet peut être de 3 types et nous devons effectuer un calcul basé sur quel type d'objet.
Nous pouvons donc implémenter en 3 classes contenant chacune une logique différente. Évidemment, l'information d'objet est disponible en runtime donc vous ne pouvez pas coder statiquement pour effectuer le calcul donc la réflexion est utilisée pour instancier l'objet de la classe dont vous avez besoin pour effectuer le calcul. objet reçu du fournisseur.


18
2018-06-22 15:35