insel - reflection api java




Was ist Reflexion und warum ist es nützlich? (14)

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.


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

Beispielsweise haben alle Objekte in Java die Methode getClass() , mit der Sie die Klasse des Objekts selbst dann bestimmen können, wenn Sie sie zum Zeitpunkt der Kompilierung nicht kennen (z. B. wenn Sie es als Object deklariert haben) Eine Reflektion ist in weniger dynamischen Sprachen wie C++ nicht möglich. 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.


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.


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

Ich möchte nur einen Punkt zu all dem hinzufügen, was aufgelistet wurde.

Mit Reflection API können Sie universelle toString() Methode für jedes Objekt schreiben.

Es ist nützlich beim Debuggen.

Hier ist ein Beispiel:

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

Java Reflection ist sehr mächtig und kann sehr nützlich sein. Java Reflection ermöglicht es , Klassen, Schnittstellen, Felder und Methoden zur Laufzeit zu untersuchen, ohne die Namen der Klassen, Methoden etc. zur Kompilierzeit zu kennen. Es ist auch möglich, neue Objekte zu instanziieren, Methoden aufzurufen und Feldwerte mittels Reflektion zu erhalten / setzen.

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

Edit : Nach fast 1 Jahr bearbeite ich diese Antwort, während ich beim Lesen über Reflektion noch ein wenig mehr Reflection benutzt habe.

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

Nach meinem Verständnis:

Reflection ermöglicht dem Programmierer den dynamischen Zugriff auf Entitäten im Programm. dh während der Programmierung 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.


Reflection bietet Ihnen die Möglichkeit, allgemeineren Code zu schreiben. Sie können das Objekt zur Laufzeit erstellen und seine Methode zur Laufzeit aufrufen. Daher kann das Programm hoch parametrisiert werden. Es erlaubt auch, das Objekt und die Klasse zu inspizieren, um ihre Variablen und Methoden zu erkennen, die der Außenwelt ausgesetzt sind.


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 ( java.lang.reflect ) nicht importieren. Auf Klassenmetadaten kann über java.lang.Class zugegriffen werden.

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


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 das 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 Spring , das Reflektion verwendet, um seine Beans zu erstellen, und für seine starke Verwendung von Proxies


Reflexion ist es, Objekt zu sehen, um ihre Erscheinung zu sehen. Dieses Argument scheint nichts mit Reflexion zu tun zu haben. In der Tat ist dies die Fähigkeit, sich selbst zu identifizieren.

Reflexion selbst ist ein Wort für solche Sprachen, denen die Fähigkeit zur Selbsterkenntnis und Selbstwahrnehmung wie Java und C # fehlt. Weil sie nicht die Fähigkeit zur Selbsterkenntnis haben, wenn wir beobachten wollen, wie es aussieht, müssen wir etwas anderes haben, um zu reflektieren, wie es aussieht. Ausgezeichnete dynamische Sprachen wie Ruby und Python können die Reflexion ihrer eigenen ohne die Hilfe anderer Individuen wahrnehmen. Wir können sagen, dass das Objekt von Java nicht wahrnehmen kann, wie es ohne einen Spiegel aussieht, der ein Objekt der Reflexionsklasse ist, aber Objekt in Python kann es ohne einen Spiegel wahrnehmen. Deshalb brauchen wir eine Reflexion in Java.


Wie der Name selbst andeutet, spiegelt er wider, was er zum Beispiel für die Klassenmethode usw. enthält, abgesehen von der Bereitstellung einer Funktion zum dynamischen Aufrufen einer Methode, die zur Laufzeit eine Instanz dynamisch erzeugt.

Es wird von vielen Frameworks und Anwendungen unter dem Holz verwendet, um Dienste aufzurufen, ohne den Code tatsächlich zu kennen.


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

}

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 aufzulisten. 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 Test Tools Debugger müssen in der Lage sein, private Mitglieder in Klassen zu untersuchen. Testkabelbäume können die Reflektion nutzen, um systematisch auf einer Klasse definierte APIs für entdeckbare Mengen 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 geringere 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


Reflection ist eine API, mit der das Verhalten von Methoden, Klassen und Interfaces zur Laufzeit untersucht oder verändert wird.

  1. Die erforderlichen Klassen für die Reflektion werden unter java.lang.reflect package .
  2. Reflection gibt uns Informationen über die Klasse, zu der ein Objekt gehört, und auch über die Methoden dieser Klasse, die mit dem Objekt ausgeführt werden können.
  3. Durch Reflektion können wir Methoden zur Laufzeit unabhängig vom verwendeten Access Specifier aufrufen.

Die Pakete java.lang und java.lang.reflect stellen Klassen für die Java-Reflektion bereit.

Reflektion kann verwendet werden, um Informationen über -

  1. Klasse Die Methode getClass() wird verwendet, um den Namen der Klasse getClass() zu der ein Objekt gehört.

  2. Konstruktoren Die Methode getConstructors() wird verwendet, um die öffentlichen Konstruktoren der Klasse getConstructors() zu der ein Objekt gehört.

  3. Methoden Die Methode getMethods() wird verwendet, um die öffentlichen Methoden der Klasse getMethods() zu der ein Objekt gehört.

Die Reflection API wird hauptsächlich verwendet in:

IDE (Integrierte Entwicklungsumgebung) zB Eclipse, MyEclipse, NetBeans etc.
Debugger und Test Tools usw.

Vorteile der Verwendung von Reflektion:

Erweiterbarkeitsfunktionen: Eine Anwendung kann externe benutzerdefinierte Klassen verwenden, indem sie Instanzen von Erweiterbarkeitsobjekten unter Verwendung ihrer vollständig qualifizierten Namen erstellt.

Debugging- und Test-Tools: Debugger verwenden die Eigenschaft Reflection, um private Mitglieder in Klassen zu untersuchen.

Nachteile:

Performance-Overhead: Reflektierende Operationen haben eine geringere Leistung als nicht reflektierende Gegenstücke und sollten in Codeabschnitten vermieden werden, die häufig in performance-sensitiven Anwendungen aufgerufen werden.

Belichtung von Interna: Reflektierender Code bricht Abstraktionen und kann daher das Verhalten bei Upgrades der Plattform ändern.

Ref: Java Reflection javarevisited.blogspot.in





terminology