Reflexion (Programmierung)
Reflexion (englisch reflection) bedeutet in der Programmierung, dass ein Programm seine eigene Struktur kennen (englisch introspection) und/oder diese modifizieren kann (englisch intercession).[1]
Details
Reflexion ermöglicht bei objektorientierter Programmierung beispielsweise zur Laufzeit die Abfrage von Informationen über die Klassen aus denen Objekte instanziiert werden. Bei einer Klasse sind dies beispielsweise der Name der Klasse, die definierten Felder und Methoden. Bei einer Methode sind das unter anderem deren Sichtbarkeit, der Datentyp des Rückgabewertes oder der Typ der Übergabeparameter. Die Umsetzung der Abfragemöglichkeiten ist sprachspezifisch.
Reflexion ist eine Voraussetzung für die dynamische Typsicherheit. Typen werden hierbei zur Ausführungszeit überprüft und müssen reflexiv abgefragt werden können. Ebenso wird sie für die Realisierung von Persistenz (persistente Datenhaltung von Objekten und deren Beziehungen) verwendet: Welche Daten zu speichern sind, kann die Persistenz-Realisierung zur Laufzeit über Reflexion erfragen. Für unterschiedliche (Objekt-)Strukturen müssen dann keinen spezifischen Speicherfunktionen implementiert werden.
Die Ausführungsgeschwindigkeit von Code per Reflexion ist für gewöhnlich geringer als die von statischem Code. Dies liegt unter anderem an den String-Vergleichen der entsprechenden Namen der gewünschten Methoden, Eigenschaften usw. mit den Einträgen in den Metadaten. Jedoch bietet Reflexion eine sehr hohe Laufzeitflexibilität, da Code dynamisch aufgerufen werden kann, neue Instanzen erstellt oder sogar Typen und Objekte dynamisch neu strukturiert werden können.
Sprachunterstützung
Für die Realisierung der Reflexion ist das Speichern von Metainformation im ausführbaren Code des Programms notwendig, welche zur Laufzeit ausgelesen wird.
Bei interpretierenden Programmiersprachen liegt zur Ausführungszeit der ursprüngliche Programmcode vor, was neben dem Zugriff auf die Strukturinformation (Methodendeklaration) auch den Zugriff auf die Implementierung ermöglicht. Beispiele dafür sind PHP, Lisp, Python, Smalltalk und Tcl.
Weiterhin wird Reflexion häufig von Frameworks oder Sprachen unterstützt, die in einer virtuellen Maschine ausgeführt werden, beispielsweise Java oder Smalltalk. Auch alle Sprachen, die das .NET Framework verwenden, müssen laut CLS (Common Language Specification) die für Reflexion notwendigen Informationen als Metadaten speichern, wie z. B. C#, Object Pascal, VB.NET oder IronPython.
Im Prinzip kann auch Maschinencode im RAM, der von einem Mikroprozessor ausgeführt wird, als reflexiv betrachtet werden. Ein solches Programm ist in der Lage, seine Anweisungen wie Daten zu behandeln und kann deshalb seine Struktur analysieren und verändern.
Beispiele
C#
Das folgende Beispiel zeigt eine Methode, die eine beliebige andere Methode eines gegebenen Objekts aufruft und deren Rückgabewert zurückgibt. Aus Gründen der Vereinfachung unterstützt dieses Beispiel nur den Aufruf von Methoden ohne Parameter, die Zeichenketten ("String") zurückgeben.
public string GetStringProperty(Object obj, string methodName) {
string val = null;
try {
MethodInfo methodInfo = obj.GetType().GetMethod(methodName);
val = (string)methodInfo.Invoke(obj, new Object[0]);
} catch (Exception e) {
//Fehlerbehandlung zwecks Übersichtlichkeit nicht implementiert.
}
return val;
}
Java
Das folgende Beispiel zeigt eine Methode, die der Funktionalität des C#-Beispiels entspricht.
public String getStringProperty(Object object, String methodName) {
String value = null;
try {
Method getter = object.getClass().getMethod(methodName, new Class[0]);
value = (String) getter.invoke(object, new Object[0]);
} catch (Exception e) {
//Fehlerbehandlung zwecks Übersichtlichkeit nicht implementiert.
}
return value;
}
Die folgende Anweisung würde dann die Methode getVorname()
des Objekts person
aufrufen und deren Rückgabewert ausgeben.
System.out.println("Vorname von " + person + " ist "
+ getStringProperty(person, "getVorname"));
Common Lisp
(funcall (find-symbol "SIN") 3)
PHP
$reflectionExampleObj = new ReflectionClass('ExampleClass');
Reflection::export($reflectionExampleObj);
Die angegebene Anweisung würde dann die Eigenschaften der Klasse "ExampleClass" als Ausgabe zurück liefern.
Die folgende Anweisung liefert beispielsweise die statischen Variablen der Klasse, wenn welche existieren:
$reflectionExampleObj = new ReflectionClass('ExampleClass');
$reflectionExampleObj->getStaticVariables();
Python
class Person(object):
def __init__(self, name):
self.name = name
def say_hello(self):
return 'Hallo %s!' % self.name
ute = Person('Ute')
# direkt
print(ute.say_hello()) # Hallo Ute!
# Reflexion entspricht ute.say_hello()
m = getattr(ute, 'say_hello')
print(m()) # Hallo Ute!
Ruby
"a String".class # ergibt "String"
"a String".respond_to?(:size) # ergibt true -> Objekt kann Methode size ausführen
"a String".methods # ergibt einen Array mit allen Methoden des Objektes
"a String".method(:concat).arity # gibt die Anzahl der Parameter an,
# die die Methode concat verlangt
class Book
def initialize(*parameters)
@title, @author, @chapters = parameters
end
end
a_book = Book.new("Book Title", "Someone", ["chapter I", "chapter II", "chapter III"])
a_book.instance_variables # ergibt einen Array aller Objektinstanzvariablen:
# ["@title", "@author", "@chapters"]
Book.instance_methods # gibt alle Instanzmethoden der Klasse Book aus.
Smalltalk
Statt Methodenaufrufen auf Objekten werden diesen in Smalltalk Nachrichten gesendet. Die Oberklasse Object
stellt die Nachricht perform:
(sowie Varianten für Aufrufe mit Parametern) zur Verfügung, mit welcher einem Objekt die zu dem übergebenen Symbol entsprechende Nachricht gesendet wird. Die folgenden Codebeispiele (für GNU Smalltalk) sind somit äquivalent:
s := Set new.
s add: 4.
s printNl.
s := Set perform: #new.
s perform: #add: with: 4.
s perform: #printNl.
Tcl
Das folgende Beispiel zeigt eine Methode rufe
, die der Funktionalität des obigen C#- und Java-Beispiels entspricht.
oo::class create Person {
variable name vorname
constructor {n v} {
set name $n
set vorname $v
}
# Diese gewöhnliche, parameterlose Methode liefert den Vornamen
method getVorname {} {
return $vorname
}
# Diese Methode ruft die Methode auf, deren Name als Parameter mitgegeben wird.
method rufe {methode} {
return [[self] $methode]
}
}
# Erzeugen eines Objekts person der Klasse Person
Person create person Meier Franz
Die folgende Anweisung würde dann die Methode getVorname
des Objekts person
aufrufen und deren Rückgabewert ausgeben.
puts "Der Vorname lautet [person rufe getVorname] ";
Weblinks
Einzelnachweise
- Walter Cazzola: "SmartReflection: Efficient Introspection in Java", in Journal of Object Technology, vol. 3, no. 11, December 2004, Special issue: OOPS track at SAC 2004, Nicosia/Cyprus, pp. 117–132