Frage Was ist Reflexion und warum ist es nützlich?


Was ist Reflexion und warum ist es nützlich?

Ich bin besonders an Java interessiert, aber ich gehe davon aus, dass die Prinzipien in jeder Sprache gleich sind.


1690
2017-09-01 08:39


Ursprung


Antworten:


Der Name reflection wird verwendet, um Code zu beschreiben, der in der Lage ist, anderen Code im selben System (oder sich selbst) zu inspizieren.

Angenommen, Sie haben ein Objekt eines unbekannten Typs in Java, und Sie möchten eine "doSomething" -Methode aufrufen, wenn eine existiert. Das statische Typisierungssystem von Java ist nicht darauf ausgelegt, dies zu unterstützen, es sei denn, das Objekt entspricht einer bekannten Schnittstelle. Durch Reflexion kann Ihr Code jedoch auf das Objekt schauen und herausfinden, ob es eine Methode mit dem Namen 'doSomething' hat möchte.

Also, um Ihnen ein Codebeispiel in Java zu geben (stellen Sie sich vor, das Objekt ist foo):

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

Ein sehr häufiger Anwendungsfall in Java ist die Verwendung mit Anmerkungen. JUnit 4 zum Beispiel verwendet Reflection, um Ihre Klassen nach Methoden zu durchsuchen, die mit der @Test-Annotation versehen sind, und ruft sie dann beim Ausführen des Unit-Tests auf.

Es gibt einige gute Reflektionsbeispiele, mit denen Sie beginnen können http://docs.oracle.com/javase/tutorial/reflect/index.html

Und schließlich, ja, die Konzepte sind in anderen statisch Typen Sprachen, die Reflexion unterstützen (wie C #) ziemlich ähnlich. In dynamisch typisierten Sprachen ist der oben beschriebene Anwendungsfall weniger notwendig (da der Compiler jede Methode für jedes Objekt aufrufen kann, die zur Laufzeit fehlschlägt, wenn sie nicht existiert), aber im zweiten Fall nach Methoden sucht, die mit oder markiert sind Arbeit in einer bestimmten Art ist immer noch üblich.

Aktualisierung von einem Kommentar:

Die Fähigkeit, den Code im System zu prüfen und Objekttypen zu sehen, ist   nicht Reflexion, sondern Typ Introspection. Reflexion ist dann die   Möglichkeit, zur Laufzeit Änderungen vorzunehmen unter Verwendung von   Selbstbeobachtung. Die Unterscheidung ist hier wie bei einigen Sprachen notwendig   unterstützt Introspektion, unterstützt aber keine Reflektion. Ein solches Beispiel   ist C ++


1414
2017-09-01 08:44



Betrachtung ist die Fähigkeit einer Sprache, Klassen, Methoden, Attribute usw. zur Laufzeit zu prüfen und dynamisch aufzurufen.

Zum Beispiel haben alle Objekte in Java die Methode getClass(), mit dem Sie die Klasse des Objekts selbst dann bestimmen können, wenn Sie sie zum Zeitpunkt der Kompilierung nicht kennen (z. B. wenn Sie sie als deklariert haben) Object) - das mag trivial erscheinen, aber eine solche Reflektion ist in weniger dynamischen Sprachen wie C++. Fortgeschrittenere Anwendungen können Methoden und Konstruktoren auflisten und aufrufen.

Reflektion ist wichtig, da Sie damit Programme schreiben können, die zur Kompilierungszeit nicht alles "wissen" müssen, wodurch sie dynamischer werden, da sie zur Laufzeit miteinander verbunden werden können. Der Code kann gegen bekannte Schnittstellen geschrieben werden, aber die tatsächlich zu verwendenden Klassen können durch Reflexion von Konfigurationsdateien instanziiert werden.

Viele moderne Frameworks verwenden Reflexion genau aus diesem Grund. Die meisten anderen modernen Sprachen verwenden ebenfalls Reflektion, und in Skriptsprachen (wie Python) sind sie noch enger integriert, da sie sich innerhalb des allgemeinen Programmiermodells dieser Sprachen natürlicher anfühlen.


195
2017-09-01 08:52



Eine meiner Lieblingsreflexionen ist die folgende Java-Dump-Methode. Es nimmt jedes Objekt als Parameter und verwendet die Java-Reflektions-API, um jeden Feldnamen und -wert auszugeben.

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



Gebrauch der Reflexion

Reflection wird häufig von Programmen verwendet, die das Laufzeitverhalten von Anwendungen untersuchen oder ändern müssen, die in der Java Virtual Machine ausgeführt werden. Dies ist eine relativ fortgeschrittene Funktion und sollte nur von Entwicklern verwendet werden, die die Grundlagen der Sprache gut verstehen. Vor diesem Hintergrund ist Reflektion eine leistungsfähige Technik und kann Anwendungen ermöglichen, Vorgänge auszuführen, die sonst unmöglich wären.

Erweiterbarkeit

Eine Anwendung kann externe benutzerdefinierte Klassen verwenden, indem sie Instanzen von Erweiterbarkeitsobjekten unter Verwendung ihrer vollständig qualifizierten Namen erstellt. Klassenbrowser und visuelle Entwicklungsumgebungen Ein Klassenbrowser muss in der Lage sein, die Mitglieder von Klassen aufzuzählen. Visuelle Entwicklungsumgebungen können davon profitieren, dass sie die in der Reflektion verfügbaren Typinformationen verwenden, um dem Entwickler beim Schreiben von korrektem Code zu helfen. Debugger und Testwerkzeuge Debugger müssen in der Lage sein, private Mitglieder in Klassen zu untersuchen. Test-Kabelbäume können die Reflektion verwenden, um systematisch eine auf einer Klasse definierte Erkennungsmenge-API aufzurufen, um eine hohe Codeabdeckung in einer Testsuite sicherzustellen.

Nachteile der Reflexion

Reflexion ist kraftvoll, sollte aber nicht wahllos verwendet werden. Wenn es möglich ist, eine Operation ohne Reflektion durchzuführen, ist es vorzuziehen, sie zu vermeiden. Die folgenden Bedenken sollten beim Zugriff auf den Code durch Reflektion beachtet werden.

  • Leistung Overhead

Da bei der Reflektion Typen verwendet werden, die dynamisch aufgelöst werden, können bestimmte Java Virtual Machine-Optimierungen nicht durchgeführt werden. Folglich haben reflektive Operationen eine langsamere Leistung als ihre nicht reflektierenden Gegenstücke und sollten in Codeabschnitten vermieden werden, die häufig in performance-sensitiven Anwendungen aufgerufen werden.

  • Sicherheitsbeschränkungen

Reflection erfordert eine Laufzeitberechtigung, die möglicherweise nicht vorhanden ist, wenn sie unter einem Sicherheitsmanager ausgeführt wird. Dies ist eine wichtige Überlegung für Code, der in einem eingeschränkten Sicherheitskontext ausgeführt werden muss, z. B. in einem Applet.

  • Exposition von Internals

Da die Reflektion Code ermöglicht, Operationen auszuführen, die in nicht reflektierendem Code illegal sind, wie z. B. Zugriff auf private Felder und Methoden, kann die Verwendung von Reflektion zu unerwarteten Nebenwirkungen führen, die Code dysfunktional machen und die Portabilität zerstören können. Reflektierender Code bricht Abstraktionen und kann daher das Verhalten bei Upgrades der Plattform ändern.

Quelle: Die Reflektions-API


55
2017-10-17 11:59



Reflektion ist ein Schlüsselmechanismus, um einer Anwendung oder einem Framework zu ermöglichen, mit Code zu arbeiten, der möglicherweise noch nicht einmal geschrieben wurde!

Nehmen Sie zum Beispiel Ihre typische web.xml-Datei. Dies enthält eine Liste von Servlet-Elementen, die geschachtelte Elemente der Servlet-Klasse enthalten. Der Servlet-Container verarbeitet die Datei web.xml und erstellt eine neue Instanz jeder Servlet-Klasse durch Reflektion.

Ein anderes Beispiel wäre die Java-API für das XML-Parsen (JAXP). Wo ein XML-Parser-Provider über bekannte Systemeigenschaften, die zum Erstellen neuer Instanzen durch Reflektion verwendet werden, "eingesteckt" wird.

Und schließlich ist das umfassendste Beispiel Frühling die Reflexion verwendet, um seine Bohnen zu erstellen, und für seine starke Verwendung von Proxies


30
2017-09-01 09:30



Nicht jede Sprache unterstützt die Reflexion, aber die Prinzipien sind in der Regel in den Sprachen, die sie unterstützen, dieselben.

Reflexion ist die Fähigkeit, über die Struktur Ihres Programms nachzudenken. Oder konkreter. Sehen Sie sich die Objekte und Klassen an, die Sie haben, und erhalten Sie programmgesteuert Informationen zu den Methoden, Feldern und Schnittstellen, die sie implementieren. Sie können auch Dinge wie Anmerkungen betrachten.

Es ist nützlich in vielen Situationen. Überall, wo Sie Klassen dynamisch in Ihren Code einfügen möchten. Viele Objekt-relationale Mapper verwenden Reflektion, um Objekte aus Datenbanken instanziieren zu können, ohne im Voraus zu wissen, welche Objekte sie verwenden werden. Plug-in-Architekturen sind ein weiterer Ort, an dem Reflexion nützlich ist. In diesen Situationen ist es wichtig, Code dynamisch zu laden und festzustellen, ob dort Typen vorhanden sind, die die richtige Schnittstelle zur Verwendung als Plugin implementieren.


27
2017-09-01 08:50



Reflection ermöglicht das Instanziieren neuer Objekte, das Aufrufen von Methoden und das dynamische Abrufen / Festlegen von Operationen für Klassenvariablen zur Laufzeit, ohne vorher über deren Implementierung Bescheid zu wissen.

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");

Im obigen Beispiel ist der Parameter null das Objekt, für das Sie die Methode aufrufen möchten. Wenn die Methode statisch ist, geben Sie null an. Wenn die Methode nicht statisch ist, müssen Sie beim Aufrufen eine gültige MyObject-Instanz anstelle von null angeben.

Mit Reflection können Sie auch auf private Member / Methoden einer Klasse zugreifen:

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);
  • Zur Inspektion von Klassen (auch als Introspektion bekannt) müssen Sie das Reflexionspaket nicht importieren (java.lang.reflect). Auf Klassenmetadaten kann über zugegriffen werden java.lang.Class.

Reflection ist eine sehr mächtige API, kann jedoch die Anwendung verlangsamen, wenn sie übermäßig verwendet wird, da sie alle Typen zur Laufzeit auflöst.


26
2017-07-08 16:12



Beispiel:
Nehmen Sie zum Beispiel eine Remote-Anwendung, die Ihrer Anwendung ein Objekt gibt, das Sie mit ihren API-Methoden erhalten. Basierend auf dem Objekt müssen Sie möglicherweise eine Art von Berechnung durchführen.
Der Anbieter garantiert, dass das Objekt aus 3 Typen bestehen kann, und wir müssen die Berechnung basierend auf dem Objekttyp durchführen.
Wir implementieren also in 3 Klassen, von denen jede eine andere Logik enthält. Offensichtlich sind die Objektinformationen in Runtime verfügbar, so dass Sie nicht statisch programmieren können, um die Berechnung durchzuführen. Daher wird das Objekt der Klasse, die Sie für die Berechnung benötigen, instantiiert Objekt vom Provider erhalten.


18
2018-06-22 15:35



Java Reflection ist sehr mächtig und kann sehr nützlich sein. Java Reflection macht es möglich um Klassen, Schnittstellen, Felder und Methoden zur Laufzeit zu untersuchen,ohne die Namen der Klassen, Methoden usw. zur Kompilierzeit zu kennen. Es ist auch möglich, instanziieren neue Objekte, rufen Methoden auf und erhalten / setzen Feldwerte durch Reflektion.

Ein kurzes Beispiel für eine Java Reflection zeigt Ihnen, wie Reflection aussieht:

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

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

In diesem Beispiel wird das Class-Objekt von der Klasse MyObject abgerufen. Mit dem Klassenobjekt erhält das Beispiel eine Liste der Methoden in dieser Klasse, iteriert die Methoden und gibt ihre Namen aus.

Wie das alles funktioniert, wird hier erklärt

Bearbeiten: Nach fast 1 Jahr bearbeite ich diese Antwort, während ich beim Lesen über Reflektion nur noch wenige Reflektionen nutze.

  • Spring verwendet eine Bean-Konfiguration wie:


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

Wenn der Spring-Kontext dieses <bean> -Element verarbeitet, wird Class.forName (String) mit dem Argument "com.example.Foo" verwendet, um diese Klasse zu instanziieren.

Es wird dann erneut die Reflektion verwenden, um den passenden Setter für das <property> -Element zu erhalten und seinen Wert auf den angegebenen Wert zu setzen.

  • Junit verwendet Reflection speziell zum Testen von Private / Protected-Methoden.

Für private Methoden,

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

Für private Bereiche,

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

16
2017-09-08 09:40



Nach meinem Verständnis:

Reflection ermöglicht dem Programmierer den dynamischen Zugriff auf Entitäten im Programm. d. h. beim Codieren einer Anwendung, wenn der Programmierer keine Kenntnis von einer Klasse oder ihren Methoden hat, kann er diese Klasse dynamisch (zur Laufzeit) unter Verwendung von Reflexion verwenden.

Es wird häufig in Szenarien verwendet, in denen sich ein Klassenname häufig ändert. Wenn eine solche Situation auftritt, ist es für den Programmierer kompliziert, die Anwendung neu zu schreiben und den Namen der Klasse immer wieder zu ändern.

Stattdessen müssen Sie sich mithilfe von Reflektion um einen möglicherweise ändernden Klassennamen kümmern.


12
2018-02-06 05:37



einfaches Beispiel für die Reflexion. In einem Schachspiel wissen Sie nicht, was zur Laufzeit vom Benutzer bewegt wird. Reflection kann verwendet werden, um Methoden aufzurufen, die zur Laufzeit bereits implementiert sind.

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);
    }

}

12
2017-11-06 04:42