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

2125

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

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

Lehane
la source
9
Pour moi, c'est un moyen d'obtenir des noms de classe lors de l'exécution et de créer des objets de cette classe.
Nabin
65
parce que c'est une question populaire, je voudrais souligner que la réflexion (sans annotations) devrait être le tout dernier outil vers lequel vous vous dirigez pour résoudre un problème. Je l'utilise et je l'adore, mais il annule tous les avantages du typage statique de Java. Si vous en avez besoin, isolez-le dans une zone aussi petite que possible (une méthode ou une classe). Il est plus acceptable de l'utiliser dans les tests que le code de production. Avec les annotations ça devrait aller - Le point principal n'est pas de spécifier les noms de classe ou de méthode comme "Strings" si vous pouvez éventuellement l'éviter.
Bill K
4
En plus du commentaire de @ BillK: la réflexion est très puissante, je l'appellerais magique. Un grand pouvoir implique de grandes responsabilités. Utilisez-le uniquement si vous savez ce que vous faites.
MC Emperor
Vous pouvez éviter de nombreux pièges liés à la réflexion à l'aide de Manifold @Jailbreak. Il fournit un accès direct et sécurisé aux champs, méthodes, etc. privés. Laissez le compilateur Java vérifier en toute sécurité votre code et laissez Manifold générer l'accès de réflexion sous-jacent pour vous. En savoir plus: manifold.systems/docs.html#type-safe-reflection
Scott

Réponses:

1717

La réflexion du nom est utilisée pour décrire le code qui est capable d'inspecter un autre code 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 prendre en charge cela à moins que l'objet ne soit 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 'doSomething' puis l'appeler si vous vouloir.

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

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

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

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

Et enfin, oui, les concepts sont à peu près similaires dans d'autres langages typés statiquement qui prennent en charge 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 permettra à n'importe quelle méthode d'être appelée sur n'importe quel objet, échouant à l'exécution si elle n'existe pas), mais le deuxième cas de recherche de méthodes qui sont marquées ou le travail d'une certaine manière est encore courant.

Mise à jour à partir d'un commentaire:

La possibilité d'inspecter le code dans le système et de voir les types d'objets n'est pas une réflexion, mais plutôt une introspection de type. La réflexion est alors la possibilité d'apporter des modifications au moment de l'exécution en utilisant l'introspection. La distinction est nécessaire ici car certains langages supportent l'introspection, mais ne supportent pas la réflexion. Un tel exemple est C ++

Matt Sheppard
la source
32
pouvez-vous s'il vous plaît expliquer quelle est la signification de ce paramètre nul dans cette ligne Méthode méthode = foo.getClass (). getMethod ("doSomething", null);
Krsna Chaitanya
54
La valeur null indique qu'aucun paramètre n'est transmis à la méthode foo. Voir docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...) pour plus de détails.
Matt Sheppard
791
Juste pour clarifier car cela a tellement de votes positifs. La possibilité d'inspecter le code dans le système et de voir les types d'objets n'est pas une réflexion, mais plutôt une introspection de type. La réflexion est alors la possibilité d'apporter des modifications au moment de l'exécution en utilisant l'introspection. La distinction est nécessaire ici car certains langages supportent l'introspection, mais ne supportent pas la réflexion. Un tel exemple est C ++.
bigtunacan
39
J'adore la réflexion mais si vous avez le contrôle sur le code, alors utiliser la réflexion comme spécifié dans cette réponse est peu logique et donc un abus - Vous devez utiliser Type Introspection (instanceof) et des types forts. S'il n'y a pas d'autre moyen que la réflexion pour faire quelque chose, c'est comme ça qu'il faut le faire. La réflexion provoque de graves chagrins parce que vous perdez tous les avantages de l'utilisation d'un langage typé statiquement. Si vous en avez besoin, vous en avez besoin, mais même dans ce cas, je considérerais une solution préemballée comme Spring ou quelque chose qui résume complètement la réflexion nécessaire - IE: laissez quelqu'un d'autre avoir les maux de tête.
Bill K
6
@bigtunacan D'où avez-vous obtenu ces informations? Je vois le terme "réflexion" utilisé dans la documentation Java officielle d'Oracle pour décrire non seulement la possibilité d'apporter des modifications au moment de l'exécution, mais également la possibilité de voir le type d'un objet. Sans oublier que la plupart des soi-disant « introspection de type » classes associées (ex: Method, Constructor, Modifier, Field, Member, à l' exception essentiellement apparemment tous Class) sont dans le java.lang.*reflect*paquet. Peut-être que le concept de «réflexion» comprend à la fois «introspection de type» et modification au moment de l'exécution?
RestInPeace
246

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

Par exemple, tous les objets en 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 un Object) - cela peut sembler trivial, mais une telle réflexion n'est pas possible dans des langues moins dynamiques comme C++. Des utilisations plus avancées vous permettent de répertorier et d'appeler des méthodes, des constructeurs, etc.

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

De nombreux cadres modernes utilisent largement la réflexion pour cette même 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 cela semble plus naturel dans le modèle de programmation général de ces langages.

Liedman
la source
2
En d'autres termes, vous pouvez créer une instance à partir de son nom qualifié et le compilateur ne s'en plaindra pas (car disons que vous utilisez juste une chaîne pour le nom de classe). Ensuite, au moment de l'exécution, si cette classe n'est pas présente, vous obtenez une exception. Vous avez en quelque sorte ignoré le compilateur dans ce cas. Pourriez-vous me donner un cas d'utilisation spécifique pour cela? Je ne peux pas imaginer quand je le choisirais.
Fernando Gabrieli
3
@FernandoGabrieli, s'il est vrai qu'il est facile de créer des erreurs d'exécution avec réflexion, il est également parfaitement possible d'utiliser la réflexion sans risquer d'exceptions d'exécution. Comme indiqué dans ma réponse, une utilisation courante de la réflexion concerne les bibliothèques ou les frameworks, qui ne peuvent explicitement pas connaître la structure de l'application au moment de la compilation, car ils sont compilés séparément de l'application. Toute bibliothèque qui utilise du "code par convention" est susceptible d'utiliser la réflexion, mais pas nécessairement des chaînes magiques.
Liedman
1
C++possède des informations sur le type d'exécution. RTTI
_Static_assert
114

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 comme 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();
}
Ben Williams
la source
8
À quoi Callcount doit-il être défini?
Tom
8
J'ai obtenu une exception dans le thread "AWT-EventQueue-0" java.lang.StackOverflowError lorsque j'ai exécuté cela.
Tom
3
@Tom callCountdoit être défini sur zéro. Sa valeur est utilisée pour déterminer combien d'onglets doivent précéder chaque ligne de sortie: chaque fois que le vidage doit vider un "sous-objet", la sortie s'imprime telle qu'elle est imbriquée dans le parent. Cette méthode s'avère utile lorsqu'elle est enveloppée dans une autre. Considérez printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny
1
Le java.lang.StackOverflowError peut être créé en cas de références circulaires, en raison de la récursion non contrôlée: buffer.append (dump (value, callCount))
Arnaud P
4
Pouvez-vous spécifiquement publier votre code dans le domaine public, s'il vous plaît?
stolsvik
85

Utilisations de la réflexion

La réflexion est couramment utilisée par les programmes qui nécessitent la possibilité d'examiner ou de modifier le comportement d'exécution des applications s'exécutant dans la machine virtuelle Java. Il s'agit d'une fonctionnalité relativement avancée et ne doit être utilisée que par des développeurs qui ont une bonne compréhension des principes fondamentaux du langage. Avec cette mise en garde à 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 complets. Navigateurs de classe et environnements de développement visuel Un navigateur de classe doit pouvoir énumérer les membres des classes. Les environnements de développement visuel peuvent bénéficier de l'utilisation des informations de type disponibles en réflexion pour aider le développeur à écrire le code correct. Débogueurs et outils de test Les débogueurs doivent pouvoir examiner les membres privés dans les classes. Les faisceaux de tests peuvent utiliser la réflexion pour appeler systématiquement un ensemble détectable d'API définies 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 via la réflexion.

  • Frais généraux de performance

La réflexion impliquant des types résolus dynamiquement, certaines optimisations de machine virtuelle 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 qui sont fréquemment appelées dans les applications sensibles aux performances.

  • Restrictions de sécurité

Reflection 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 dans 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

Étant donné que la réflexion permet au code d'effectuer des opérations qui seraient illégales dans le code non réfléchissant, telles que l'accès aux 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échissant rompt les abstractions et peut donc changer de comportement avec les mises à niveau de la plateforme.

source: l' API Reflection

Desmond Smith
la source
44

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. Celui-ci contiendra une liste d'éléments de servlet, qui contiennent des éléments imbriqués de classe de servlet. Le conteneur de servlet traitera le fichier web.xml et créera une nouvelle instance de chaque classe de servlet par réflexion.

Un autre exemple serait l'API Java pour l'analyse 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 Spring qui utilise la réflexion pour créer ses beans, et pour son utilisation intensive de proxy

boîte à outils
la source
36

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

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

C'est utile dans beaucoup de situations. Partout où vous voulez pouvoir brancher dynamiquement des classes dans votre code. De nombreux mappeurs relationnels d'objets utilisent la réflexion pour pouvoir 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. Être capable de charger dynamiquement du code et de déterminer s'il existe des types qui implémentent la bonne interface à utiliser en tant que plugin est important dans ces situations.

Mendelt
la source
J'ai besoin d'instancier des objets en fonction des données présentes dans DB. Je pense que c'est ce que vous dites. Un exemple de code m'aiderait beaucoup. Merci d'avance.
Atom
34

La réflexion permet l'instanciation de nouveaux objets, l'invocation de méthodes et les opérations get / set sur les variables de classe dynamiquement au moment de l'exécution sans avoir une connaissance préalable 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 souhaitez appeler la méthode. Si la méthode est statique, vous fournissez null. Si la méthode n'est pas statique, alors lors de l'appel, vous devez fournir une instance MyObject valide au lieu de null.

La réflexion vous permet également d'accéder aux membres / méthodes privés 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ée introspection), vous n'avez pas besoin d'importer le package de réflexion ( java.lang.reflect). Les métadonnées de classe sont accessibles 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.

Nikhil Shekhar
la source
21

Java Reflection est assez puissant et peut être très utile. Java Reflection permet d'inspecter les classes, les interfaces, les champs et les méthodes lors de l'exécution, sans connaître les noms des classes, méthodes, etc. au moment de la compilation. Il est également possible d' instancier de nouveaux objets, d'appeler des méthodes et d'obtenir / définir des valeurs de champ à l'aide de la réflexion.

Un exemple rapide de réflexion Java pour vous montrer à quoi ressemble la réflexion:

Method[] methods = MyObject.class.getMethods();

    for(Method method : methods){
        System.out.println("method = " + method.getName());
    }

Cet exemple obtient l'objet Class de la classe appelée MyObject. En utilisant l'objet class, l'exemple obtient une liste des méthodes de cette classe, itère les méthodes et imprime leurs noms.

Exactement comment tout cela fonctionne est expliqué ici

Edit : Après presque 1 an, je modifie cette réponse car en lisant sur la réflexion, j'ai eu peu d'autres utilisations de la réflexion.

  • Spring utilise une configuration de bean telle que:


<bean id="someID" class="com.example.Foo">
    <property name="someField" value="someValue" />
</bean>

Lorsque le contexte Spring traite cet élément <bean>, il utilise Class.forName (String) avec l'argument "com.example.Foo" pour instancier cette classe.

Il utilisera ensuite à nouveau la réflexion pour obtenir le setter approprié pour l'élément <property> et définir sa valeur sur la valeur spécifiée.

  • Junit utilise Reflection en particulier pour tester les méthodes privées / protégées.

Pour les méthodes privées,

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

Pour les champs privés,

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
VdeX
la source
21

Exemple:

Prenons par exemple une application distante qui donne à votre application un objet que vous obtenez en utilisant leurs méthodes API. Maintenant, en fonction de 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 en fonction de ce type d'objet.

Nous pouvons donc implémenter dans 3 classes contenant chacune une logique différente. Évidemment, les informations sur l'objet sont disponibles au moment de l'exécution, vous ne pouvez donc 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 en fonction de la objet reçu du fournisseur.

human.js
la source
J'ai besoin de quelque chose de similaire .. Un exemple m'aiderait beaucoup car je suis nouveau dans les concepts de réflexion ..
Atom
2
Je suis confus: ne pouvez-vous pas utiliser instanceofpour déterminer le type d'objet lors de l'exécution?
ndm13
19

Exemple simple de réflexion. Dans une partie d'échecs, vous ne savez pas ce qui sera déplacé par l'utilisateur au moment de l'exécution. la réflexion peut être utilisée pour appeler des méthodes déjà implémentées au moment de l'exécution:

public class Test {

    public void firstMoveChoice(){
        System.out.println("First Move");
    } 
    public void secondMOveChoice(){
        System.out.println("Second Move");
    }
    public void thirdMoveChoice(){
        System.out.println("Third Move");
    }

    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
        Test test = new Test();
        Method[] method = test.getClass().getMethods();
        //firstMoveChoice
        method[0].invoke(test, null);
        //secondMoveChoice
        method[1].invoke(test, null);
        //thirdMoveChoice
        method[2].invoke(test, null);
    }

}
Isuru Jayakantha
la source
18

Reflection est une API utilisée pour examiner ou modifier le comportement des méthodes, des classes et des interfaces lors de l'exécution.

  1. Les classes de réflexion requises sont fournies ci-dessous java.lang.reflect package.
  2. La réflexion nous donne des informations sur la classe à laquelle appartient un objet ainsi que les méthodes de cette classe qui peuvent être exécutées en utilisant l'objet.
  3. Grâce à la réflexion, nous pouvons invoquer des méthodes lors de l'exécution, quel que soit le spécificateur d'accès utilisé avec elles.

Les packages java.langet java.lang.reflectfournissent des classes pour la réflexion Java.

La réflexion peut être utilisée pour obtenir des informations sur -

  1. Classe La getClass()méthode est utilisée pour obtenir le nom de la classe à laquelle appartient un objet.

  2. Constructeurs La getConstructors()méthode est utilisée pour obtenir les constructeurs publics de la classe à laquelle appartient un objet.

  3. Méthodes La getMethods()méthode est utilisée pour obtenir les méthodes publiques de la classe à laquelle appartient un objet.

L' API Reflection est principalement utilisée dans:

IDE (Integrated Development Environment), par exemple Eclipse, MyEclipse, NetBeans, etc.
Débogueur et outils de test, etc.

Avantages de l'utilisation de la réflexion:

Fonctions d'extensibilité: une application peut utiliser des classes externes définies par l'utilisateur en créant des instances d'objets d'extensibilité à l'aide de leurs noms complets.

Outils de débogage et de test: les débogueurs utilisent la propriété de réflexion pour examiner les membres privés des classes.

Désavantages:

Surcharge des performances: 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 qui sont fréquemment appelées dans les applications sensibles aux performances.

Exposition des internes: le code réfléchissant rompt les abstractions et peut donc modifier le comportement avec les mises à niveau de la plate-forme.

Réf: Java Reflection javarevisited.blogspot.in

roottraveller
la source
4
J'ajouterais aux inconvénients " Ça casse le refactoring ". Pour moi, c'est la principale raison d'éviter autant que possible la réflexion.
SantiBailors
Cela nous permet donc (par exemple) d'inspecter les classes que nous avons (que nous en ayons ou non), n'est-ce pas? J'entends par là, obtenir leurs méthodes ou constructeurs et les utiliser pour créer de nouvelles instances / les invoquer. Pourquoi dit-on "changer le comportement du programme" si le comportement est déjà là mais avec un code différent? Pourquoi est-il appelé "réflexion"? Merci
Fernando Gabrieli
15

Selon ma compréhension:

La réflexion permet au programmeur d'accéder dynamiquement aux entités du programme. c'est-à-dire tout en codant une application si le programmeur n'est pas au courant d'une classe ou de ses méthodes, il peut utiliser cette classe dynamiquement (au moment de l'exécution) en utilisant la réflexion.

Il est fréquemment utilisé dans les scénarios où un nom de classe change fréquemment. Si une telle situation se produit, il est alors compliqué pour le programmeur de réécrire l'application et de changer encore et encore le nom de la classe.

Au lieu de cela, en utilisant la réflexion, il est nécessaire de s'inquiéter d'un éventuel changement de nom de classe.

pramod
la source
15

La réflexion est un ensemble de fonctions qui vous permet d'accéder aux informations d'exécution de votre programme et de modifier son comportement (avec certaines limitations).

Il est utile car il vous permet de modifier le comportement d'exécution en fonction des métadonnées de votre programme, c'est-à-dire que vous pouvez vérifier le type de retour d'une fonction et changer la façon dont vous gérez la situation.

En C # par exemple, vous pouvez charger un assembly (un .dll) en runtime et l'examiner, en parcourant les classes et en prenant des actions en fonction de ce que vous avez trouvé. Il vous permet également de créer une instance d'une classe lors de l'exécution, d'appeler sa méthode, etc.

Où peut-il être utile? N'est pas utile à chaque fois, mais pour des situations concrètes. Par exemple, vous pouvez l'utiliser pour obtenir le nom de la classe à des fins de journalisation, pour créer dynamiquement des gestionnaires d'événements en fonction de ce qui est spécifié dans un fichier de configuration, etc.

Jorge Córdoba
la source
11

Je veux juste ajouter quelque chose à tout ce qui était répertorié.

Avec l' API Reflection, vous pouvez écrire une toString()méthode universelle pour n'importe quel objet.

Il est utile lors du débogage.

Voici un exemple:

class ObjectAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * Converts an object to a string representation that lists all fields.
    * @param obj an object
    * @return a string with the object's class name and all field names and
    * values
    */
   public String toString(Object obj) {
      if (obj == null) return "null";
      if (visited.contains(obj)) return "...";
      visited.add(obj);
      Class cl = obj.getClass();
      if (cl == String.class) return (String) obj;
      if (cl.isArray()) {
         String r = cl.getComponentType() + "[]{";
         for (int i = 0; i < Array.getLength(obj); i++) {
            if (i > 0) r += ",";
            Object val = Array.get(obj, i);
            if (cl.getComponentType().isPrimitive()) r += val;
            else r += toString(val);
         }
         return r + "}";
      }

      String r = cl.getName();
      // inspect the fields of this class and all superclasses
      do {
         r += "[";
         Field[] fields = cl.getDeclaredFields();
         AccessibleObject.setAccessible(fields, true);
         // get the names and values of all fields
         for (Field f : fields) {
            if (!Modifier.isStatic(f.getModifiers())) {
               if (!r.endsWith("[")) r += ",";
               r += f.getName() + "=";
               try {
                  Class t = f.getType();
                  Object val = f.get(obj);
                  if (t.isPrimitive()) r += val;
                  else r += toString(val);
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         }
         r += "]";
         cl = cl.getSuperclass();
      } while (cl != null);

      return r;
   }    
}
nazar_art
la source
11

La réflexion consiste à laisser un objet voir son apparence. Cet argument ne semble pas lié à la réflexion. En fait, c'est la capacité "d'auto-identification".

La réflexion elle-même est un mot pour ces langages qui manquent de capacité de connaissance de soi et d'auto-détection comme Java et C #. Parce qu'ils n'ont pas la capacité de se connaître, quand nous voulons observer à quoi ça ressemble, nous devons avoir une autre chose pour réfléchir à quoi ça ressemble. D'excellents langages dynamiques tels que Ruby et Python peuvent percevoir la réflexion de leur propre sans l'aide d'autres individus. Nous pouvons dire que l'objet de Java ne peut pas percevoir à quoi il ressemble sans miroir, qui est un objet de la classe de réflexion, mais un objet en Python peut le percevoir sans miroir. C'est pourquoi nous avons besoin d'une réflexion en Java.

Marcus Thornton
la source
type (), isinstance (), callable (), dir () et getattr (). .... ce sont des appels de réflexion pythonique
AnthonyJClink
9

Depuis la page de documentation java

java.lang.reflectpackage fournit des classes et des interfaces pour obtenir des informations réfléchissantes sur les classes et les objets. La réflexion permet un accès par programme aux informations sur les champs, les méthodes et les constructeurs des classes chargées, et l'utilisation de champs, de méthodes et de constructeurs réfléchis pour opérer sur leurs homologues sous-jacents, dans les limites de la sécurité.

AccessibleObjectpermet la suppression des contrôles d'accès si le nécessaire ReflectPermissionest disponible.

Les classes de ce package, ainsi que java.lang.Classles applications adaptées telles que les débogueurs, les interprètes, les inspecteurs d'objets, les navigateurs de classe et les services tels que Object Serializationet JavaBeansqui ont besoin d'accéder aux membres publics d'un objet cible (en fonction de sa classe d'exécution) ou aux membres déclarés par une classe donnée

Il comprend les fonctionnalités suivantes.

  1. Obtention d'objets Class,
  2. Examiner les propriétés d'une classe (champs, méthodes, constructeurs),
  3. Définition et obtention de valeurs de champ,
  4. Invoquer des méthodes,
  5. Création de nouvelles instances d'objets.

Jetez un œil à ce lien de documentation pour les méthodes exposées par Classclasse.

De cet article (par Dennis Sosnoski, président, Sosnoski Software Solutions, Inc) et de cet article (explorations de sécurité en pdf):

Je peux voir des inconvénients considérables par rapport à l'utilisation de Reflection

Utilisateur de la réflexion:

  1. Il fournit un moyen très polyvalent de relier dynamiquement les composants du programme
  2. Il est utile pour créer des bibliothèques qui fonctionnent avec des objets de manière très générale

Inconvénients de la réflexion:

  1. La réflexion est beaucoup plus lente que le code direct lorsqu'il est utilisé pour l'accès aux champs et aux méthodes.
  2. Il peut masquer ce qui se passe réellement dans votre code
  3. Il contourne le code source peut créer des problèmes de maintenance
  4. Le code de réflexion est également plus complexe que le code direct correspondant
  5. Il permet la violation des principales contraintes de sécurité Java telles que la protection de l'accès aux données et la sécurité des types

Abus généraux:

  1. Chargement de classes restreintes,
  2. Obtention de références aux constructeurs, méthodes ou champs d'une classe restreinte,
  3. Création de nouvelles instances d'objet, invocation de méthodes, obtention ou définition de valeurs de champ d'une classe restreinte.

Jetez un oeil à cette question SE concernant la fonctionnalité d'abus de réflexion:

Comment lire un champ privé en Java?

Sommaire:

L'utilisation non sécurisée de ses fonctions à partir d'un code système peut également facilement conduire à la compromission d'un mode de sécurité Java l. Utilisez donc cette fonctionnalité avec parcimonie

Ravindra babu
la source
Un moyen d'éviter les problèmes de performances de Reflection dans certains cas consiste à demander à une classe d' Woozleexaminer d'autres classes au démarrage pour voir lesquelles ont une RegisterAsWoozleHelper()méthode statique et d'appeler toutes ces méthodes qu'elle trouve avec un rappel qu'elles peuvent utiliser pour se raconter Woozle, en évitant besoin d'utiliser Reflection tout en désérialisant par exemple les données.
supercat
9

Comme son nom l'indique, il reflète ce qu'il contient, par exemple la méthode de classe, etc., en plus de fournir une fonctionnalité pour appeler la méthode créant une instance dynamiquement au moment de l'exécution.

Il est utilisé par de nombreux frameworks et applications sous le bois pour appeler des services sans vraiment connaître le code.

Mohammed Sarfaraz
la source
7

La réflexion vous donne la possibilité d'écrire du code plus générique. Il vous permet de créer un objet lors de l'exécution et d'appeler sa méthode lors de l'exécution. Par conséquent, le programme peut être rendu hautement paramétré. Il permet également d'introspectionner l'objet et la classe pour détecter ses variables et sa méthode exposées au monde extérieur.

saumik gupta
la source
6

Reflectiona de nombreuses utilisations . Celui que je connais le mieux, c'est de pouvoir créer du code à la volée.

IE: classes dynamiques, fonctions, constructeurs - basés sur toutes les données (résultats xml / array / sql / hardcoded / etc ..)

Ess Kay
la source
1
Cette réponse serait bien meilleure si vous donniez un seul exemple inhabituel de code généré à partir d'un résultat SQL ou d'un fichier XML, etc.
ThisClark
Aucun problème. J'ai utilisé la réflexion dans une application Windows qui génère dynamiquement l'interface basée sur XML extrait d'une base de données.
Ess Kay
Donc, fondamentalement, il y a une classe que j'ai créée qui montre à l'utilisateur un rapport. Ce rapport a des paramètres tels que Date (du au) id, ou autre. Ces informations sont stockées en xml. Nous avons donc d'abord une sélection de rapports. Sur la base du rapport sélectionné, le formulaire obtient le xml. Une fois le xml récupéré, il utilise la réflexion pour créer une classe avec des champs basés sur des types réfléchis. Une fois que vous passez à un autre rapport, l'ardoise est nettoyée et de nouveaux champs sont générés en fonction du xml. C'est donc essentiellement une forme dynamique basée sur la réflexion. J'ai également utilisé d'autres façons, mais cela devrait suffire. Espoir qui aide
Ess Kay
3

Je veux répondre à cette question par l'exemple. Tout d'abord, le Hibernateprojet utilise Reflection APIpour générer des CRUDinstructions pour combler le gouffre entre l'application en cours d'exécution et le magasin de persistance. Lorsque les choses changent dans le domaine, le Hibernatedoit les connaître pour les conserver dans le magasin de données et vice versa.

Fonctionne également Lombok Project. Il injecte simplement du code au moment de la compilation, ce qui entraîne l'insertion de code dans vos classes de domaine. (Je pense que c'est OK pour les getters et les setters)

Hibernatechoisi reflectioncar il a un impact minimal sur le processus de génération d'une application.

Et à partir de Java 7, nous avons MethodHandles, qui fonctionne comme Reflection API. Dans les projets, pour travailler avec les enregistreurs, il suffit de copier-coller le code suivant:

Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

Parce qu'il est difficile de faire une faute de frappe dans ce cas.

BSeitkazin
la source
3

Comme je trouve préférable de l'expliquer par l'exemple et aucune des réponses ne semble le faire ...

Un exemple pratique d'utilisation des réflexions serait un serveur de langage Java écrit en Java ou un serveur de langage PHP écrit en PHP, etc. Afin d'avoir tous les noms de balises (mots qui peuvent être remplis automatiquement) pour afficher toutes les correspondances possibles lors de la frappe, le serveur de langue doit inspecter tout ce qui concerne la classe, y compris les blocs doc et les membres privés. Pour cela, il a besoin d'un reflet de ladite classe.

Un autre exemple serait un test unitaire d'une méthode privée. Une façon de le faire est de créer une réflexion et de changer la portée de la méthode au public dans la phase de configuration du test. Bien sûr, on peut affirmer que les méthodes privées ne doivent pas être testées directement, mais ce n'est pas le but.

cprn
la source