2558 Stimmen

Was ist Reflexion und warum ist sie nützlich?

Was ist Reflexion, und warum ist sie nützlich?

Ich interessiere mich besonders für Java aber ich gehe davon aus, dass die Grundsätze in jeder Sprache die gleichen sind.

2023voto

Matt Sheppard Punkte 113439

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

Nehmen wir an, Sie haben ein Objekt eines unbekannten Typs in Java und möchten eine "doSomething"-Methode dafür aufrufen, falls eine solche existiert. Das statische Typisierungssystem von Java ist nicht wirklich dafür ausgelegt, dies zu unterstützen, es sei denn, das Objekt entspricht einer bekannten Schnittstelle, aber mit Hilfe von Reflection kann Ihr Code das Objekt betrachten und herausfinden, ob es eine Methode namens "doSomething" hat, und diese dann aufrufen, wenn Sie das möchten.

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

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

Ein sehr häufiger Anwendungsfall in Java ist die Verwendung von Annotationen. JUnit 4 verwendet beispielsweise Reflection, um Ihre Klassen nach Methoden zu durchsuchen, die mit der @Test-Annotation versehen sind, und ruft diese dann bei der Ausführung des Einheitstests auf.

Für den Anfang gibt es einige gute Beispiele für Reflexionen unter http://docs.oracle.com/javase/tutorial/reflect/index.html

Und schließlich, ja, die Konzepte sind in anderen statisch typisierten Sprachen, die Reflexion unterstützen (wie C#), ziemlich ähnlich. In dynamisch typisierten Sprachen ist der oben beschriebene Anwendungsfall weniger notwendig (da der Compiler es zulässt, dass eine beliebige Methode für ein beliebiges Objekt aufgerufen wird und zur Laufzeit fehlschlägt, wenn es nicht existiert), aber der zweite Fall der Suche nach Methoden, die markiert sind oder auf eine bestimmte Weise funktionieren, ist immer noch üblich.

Aktualisierung aufgrund eines Kommentars:

Die Möglichkeit, den Code im System zu inspizieren und Objekttypen zu sehen, ist nicht Reflexion, sondern Typ-Introspektion. Reflexion ist dann die die Möglichkeit, Änderungen zur Laufzeit vorzunehmen, indem man die Introspektion. Die Unterscheidung ist hier notwendig, da einige Sprachen Introspektion unterstützen, aber keine Reflexion. Ein solches Beispiel ist C++

299voto

Liedman Punkte 9809

Reflexion ist die Fähigkeit einer Sprache, Klassen, Methoden, Attribute usw. zur Laufzeit zu inspizieren und dynamisch aufzurufen.

Zum Beispiel haben alle Objekte in Java die Methode getClass() die es Ihnen ermöglicht, die Klasse des Objekts zu bestimmen, auch wenn Sie sie zur Kompilierzeit nicht kennen (z. B. wenn Sie es als Object ) - dies mag trivial erscheinen, aber eine solche Reflexion ist in weniger dynamischen Sprachen wie C++ . Bei fortgeschrittenen Anwendungen können Sie Methoden, Konstruktoren usw. auflisten und aufrufen.

Die Reflexion ist wichtig, da sie es ermöglicht, Programme zu schreiben, die zur Kompilierungszeit nicht alles "wissen" müssen, wodurch sie dynamischer werden, da sie zur Laufzeit miteinander verknüpft werden können. Der Code kann gegen bekannte Schnittstellen geschrieben werden, aber die tatsächlich zu verwendenden Klassen können mittels Reflection aus Konfigurationsdateien instanziiert werden.

Viele moderne Frameworks verwenden aus diesem Grund Reflection in großem Umfang. Die meisten anderen modernen Sprachen verwenden Reflection ebenfalls, und in Skriptsprachen (wie Python) sind sie sogar noch enger integriert, da es sich im allgemeinen Programmiermodell dieser Sprachen natürlicher anfühlt.

127voto

Ben Williams Punkte 2267

Eine meiner Lieblingsanwendungen von Reflexion ist die unten stehende Java-Dump-Methode. Sie nimmt ein beliebiges Objekt als Parameter und verwendet die Java-Reflection-API, um jeden Feldnamen und -wert auszudrucken.

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

101voto

Desmond Smith Punkte 1109

Anwendungen der Reflexion

Reflection wird häufig von Programmen verwendet, die die Möglichkeit benötigen, das Laufzeitverhalten von Anwendungen, die in der virtuellen Java-Maschine laufen, zu untersuchen oder zu ändern. Es handelt sich hierbei um eine relativ fortgeschrittene Funktion, die nur von Entwicklern verwendet werden sollte, die die Grundlagen der Sprache gut beherrschen. Mit diesem Vorbehalt im Hinterkopf ist Reflection eine leistungsfähige Technik, die es Anwendungen ermöglichen kann, Operationen durchzuführen, die sonst unmöglich wären.

Erweiterbare Funktionen

Eine Anwendung kann externe, benutzerdefinierte Klassen verwenden, indem sie Instanzen von Erweiterungsobjekten unter Verwendung ihrer vollqualifizierten Namen erzeugt. Klassenbrowser und visuelle Entwicklungsumgebungen Ein Klassenbrowser muss in der Lage sein, die Mitglieder von Klassen aufzuzählen. Visuelle Entwicklungsumgebungen können von der Nutzung der in Reflection verfügbaren Typinformationen profitieren, um den Entwickler beim Schreiben von korrektem Code zu unterstützen. Debugger und Testwerkzeuge Debugger müssen in der Lage sein, private Mitglieder in Klassen zu untersuchen. Test-Harnesse können Reflection nutzen, um systematisch eine Reihe von auffindbaren APIs aufzurufen, die für eine Klasse definiert sind, um einen hohen Grad an Codeabdeckung in einer Test-Suite zu gewährleisten.

Die Nachteile der Reflexion

Reflexion ist mächtig, sollte aber nicht wahllos eingesetzt werden. Wenn es möglich ist, eine Operation ohne Reflection auszuführen, sollte man sie lieber vermeiden. Beim Zugriff auf den Code über Reflection sollten die folgenden Punkte beachtet werden.

  • Performance-Mehrkosten

Da Reflection Typen beinhaltet, die dynamisch aufgelöst werden, können bestimmte Optimierungen der virtuellen Java-Maschine nicht durchgeführt werden. Folglich sind reflexive Operationen langsamer als ihre nicht-reflektierenden Gegenstücke und sollten in Codeabschnitten, die in leistungsempfindlichen Anwendungen häufig aufgerufen werden, vermieden werden.

  • Sicherheitsbeschränkungen

Reflection erfordert eine Laufzeitberechtigung, die bei der Ausführung unter einem Sicherheitsmanager möglicherweise nicht vorhanden ist. Dies ist ein wichtiger Aspekt für Code, der in einem eingeschränkten Sicherheitskontext ausgeführt werden muss, wie z. B. in einem Applet.

  • Offenlegung von Interna

Da Reflection Code die Durchführung von Operationen ermöglicht, die in nicht-reflective Code illegal wären, wie z.B. der Zugriff auf private Felder und Methoden, kann die Verwendung von Reflection zu unerwarteten Nebeneffekten führen, die den Code dysfunktional machen und die Portabilität zerstören können. Reflektierender Code bricht Abstraktionen auf und kann daher das Verhalten bei Upgrades der Plattform ändern.

Quelle: Die Reflection-API

51voto

toolkit Punkte 48653

Reflection ist ein wichtiger Mechanismus, der es einer Anwendung oder einem Framework ermöglicht, mit Code zu arbeiten, der möglicherweise noch gar nicht geschrieben wurde!

Nehmen Sie zum Beispiel Ihre typische web.xml-Datei. Diese enthält eine Liste von Servlet-Elementen, die verschachtelte Servlet-Klassen-Elemente enthalten. Der Servlet-Container verarbeitet die Datei web.xml und erstellt durch Reflection eine neue Instanz der einzelnen Servlet-Klassen.

Ein weiteres Beispiel wäre die Java-API für XML-Parsing (JAXP) . Ein XML-Parser-Anbieter wird über bekannte Systemeigenschaften "eingesteckt", die zur Konstruktion neuer Instanzen durch Reflexion verwendet werden.

Und schließlich ist das umfassendste Beispiel Frühling das Reflection zur Erstellung seiner Beans verwendet und stark auf Proxys setzt

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X