Reflection (Reflexion) ist ein Laufzeitmechanismus, der es dem Code ermöglicht, seine eigene Struktur zu untersuchen: Klassen, Methoden, Felder und Annotationen zu erhalten, ohne die Typen zur Kompilierzeit zu kennen. Dieses Werkzeug liegt vielen mobilen Frameworks zugrunde — der JSON-Serialisierung (Gson, Moshi), der Dependency Injection (Dagger, Koin) und den Test-Runnern (JUnit, XCTest). Laut dem Oracle Java Reflection Tutorial, 2024 ist Reflection ein obligatorisches Element der Java-Plattform, das von allen großen Bibliotheken verwendet wird.
Wichtigste Punkte
Reflection ist die Fähigkeit eines Programms, seine eigene Struktur und sein Verhalten während der Ausführung zu beobachten und zu modifizieren. In objektorientierten Sprachen bedeutet das, Class-, Method-, Field- und Constructor-Objekte zu erhalten, die Programmelemente als Daten darstellen, die zum Lesen und Aufrufen verfügbar sind.
Der Begriff „reflection“ wurde 1982 in der Community für künstliche Intelligenz eingeführt (Brian Cantwell Smith) und in der Sprache Smalltalk implementiert. In der mobilen Entwicklung erschien Reflection erstmals in Java ME und Objective-C (1986, NextStep). Heute verfügt jede große mobile Plattform über eine eigene Reflection-API: Java/Kotlin für Android, Objective-C Runtime für iOS, Swift Mirror API für Swift.
Der Reflection-Mechanismus basiert auf Metadaten, die der Compiler im Bytecode oder in der Binärdatei speichert. Android speichert vollständige Informationen über Klassen in DEX-Dateien, iOS in der Sektion __objc_classlist des Mach-O-Segments. Die Laufzeitumgebung lädt diese Metadaten in den Speicher und stellt eine API zum Durchlaufen bereit.
Die Java Reflection API ist um die Klasse java.lang.Class aufgebaut. Jedes Objekt in Java kann über .getClass() oder Class.forName() in eine Class umgewandelt werden. Aus Class werden alle Methoden, Felder, Konstruktoren, Annotationen und Superklassen extrahiert. Kotlin erbt die Java-Reflexion und fügt eigene KClass, KFunction, KProperty aus dem Paket kotlin.reflect hinzu.
import kotlin.reflect.full.declaredMemberFunctions
data class User(
val name: String,
val email: String
)
fun inspectClass() {
val kClass = User::class
val properties = kClass.declaredMemberProperties
val functions = kClass.declaredMemberFunctions
properties.forEach { prop ->
println("Eigenschaft: ${prop.name}, Typ: ${prop.returnType}")
}
}
In diesem Beispiel liefert KClass die Metadaten der Data Class User. declaredMemberProperties gibt eine Liste von Eigenschaften mit ihren Typen und Gettern zurück. Die Kotlin-Reflexion ist eng mit Koroutinen integriert: KFunction unterstützt den Modifikator suspend, wodurch asynchrone Methoden über Reflection aufgerufen werden können.
Die Java-Reflexion arbeitet mit Class<?>, Method.setAccessible() und Field.get(). setAccessible(true) deaktiviert die Zugriffskontrollprüfungen der Sprache Java für private Elemente. Das ist ein mächtiger, aber gefährlicher Mechanismus: Auf Android ab API 28 kann der Aufruf von setAccessible auf versteckte Systemmethoden eine InaccessibleObjectException auslösen.
// Java reflection: Aufruf einer privaten Methode
Class> clazz = Class.forName("com.example.MyClass");
Object instance = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("privateMethod", String.class);
method.setAccessible(true);
method.invoke(instance, "reflection test");
Der Code demonstriert Class.forName() — das dynamische Laden einer Klasse über einen String-Namen. Das ist die Grundlage von Plugin-Architekturen: Eine Klasse kann zur Kompilierzeit unbekannt sein, aber zur Laufzeit über Reflection geladen und ausgeführt werden. getDeclaredMethod(“privateMethod”, ...) findet eine Methode über Namen und Parametertypen, und invoke führt sie aus.
Die Objective-C-Runtime stellt die Funktionen class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject bereit. Im Gegensatz zu Java verbirgt Objective-C private Methoden nicht standardmäßig — die Laufzeitumgebung sieht alle Methoden der Klasse. Das erklärt, warum Method Swizzling ohne setAccessible funktioniert: Die Laufzeitumgebung hat auf Metadatenebene keine Kapselung.
Reflection wird in wichtigen Bibliotheken der mobilen Entwicklung verwendet. Die JSON-Serialisierung (Gson, Moshi, Kotlinx.serialization) erhält Objekteigenschaften über Reflection und gleicht sie mit JSON-Schlüsseln ab. Dependency Injection (Dagger, Koin, Swinject) analysiert Konstruktoren und Felder für die automatische Abhängigkeitsinjektion. ORM-Bibliotheken (Room, Realm) nutzen Reflection, um Klassen auf Datenbanktabellen abzubilden.
Jede dieser Anwendungen funktioniert genau zur Laufzeit — der Code weiß nicht im Voraus, auf welche Klassen er stoßen wird. Reflection bietet einen universellen Mechanismus, um diese Ungewissheit zu überwinden, auf Kosten von Leistung und Sicherheit.
Reflection ist 10–100 Mal langsamer als direkte Methodenaufrufe. Der Grund sind fehlende JIT-Optimierungen (Devirtualisierung, Inlining), Typprüfungen bei jedem Aufruf und das Verpacken von Parametern in Object[]/varargs. ART auf Android 14 kann Reflection-Aufrufe nicht per Inline optimieren, da die Zielmethode bis zum Ausführungszeitpunkt unbekannt ist.
| Operation | Direkter Aufruf | Über Reflection | Verlangsamung |
|---|---|---|---|
| Methodenaufruf ohne Parameter | ~3 ns | ~120 ns | 40x |
| Lesen eines int-Felds | ~1 ns | ~85 ns | 85x |
| Methodenaufruf mit 2 Parametern | ~4 ns | ~250 ns | 62x |
| Instanzerstellung über den Konstruktor | ~5 ns | ~180 ns | 36x |
| Auflösung einer Klasse über String | — | ~800 ns | — |
Die Daten wurden auf einem Google Pixel 8 (Android 14, ART) ermittelt. Die Leistung von Reflection verbessert sich mit jeder Android-Version: Auf Android 9 war ein Aufruf über Method.invoke() 150 Mal langsamer als ein direkter, auf Android 14 ist er 40 Mal langsamer. ART verwendet eingebaute Method-Handle-Mechanismen zur Optimierung.
Für leistungskritische Abschnitte ersetzen Entwickler Reflection durch Codegenerierung: Dagger verwendet Annotation Processing statt Laufzeitsuche, Kotlinx.serialization generiert Serializer über KSP, Moshi passt @JsonClass(generateAdapter = true) für Codegen zur Kompilierzeit an.
Annotation Processing (KAPT, KSP) und Codegenerierung sind die wichtigsten Alternativen zu Reflection in der mobilen Entwicklung. Sie verlagern die Metadatenanalyse von der Laufzeit zur Kompilierzeit: Der Code wird vor dem Start der App generiert, was den Reflection-Overhead beseitigt und die Leistung verbessert.
// KSP: Codegenerierung statt Reflection
@Serializable
data class Config(
val apiUrl: String,
val timeout: Int
)
// KSP generiert ConfigSerializer ohne Reflection
fun loadConfig(json: String): Config {
return Config.serializer().decodeFromString(json)
}
In diesem Beispiel ist @Serializable die Annotation von Kotlinx.serialization. KSP (Kotlin Symbol Processing) analysiert den Quellcode zur Kompilierzeit, findet alle @Serializable-Klassen und generiert die Serializer. Während der Ausführung der Anwendung wird keine Reflection verwendet — der Serializer ist bereits in Maschinencode kompiliert.
Codegenerierung bietet bessere Leistung, Typsicherheit und eine kleinere Binärdatei (Dead Code Elimination entfernt ungenutzte Reflection-Abhängigkeiten). Reflection bleibt für Aufgaben notwendig, bei denen Typen zur Kompilierzeit unbekannt sind: dynamisches Laden von Plugins, Laufzeit-Proxys, Testinstrumentierung. Laut Kotlin ist Kotlinx.serialization mit KSP 3–5 Mal schneller als Gson, das auf Reflection basiert.
Reflection auf mobilen Plattformen hat Sicherheits- und Leistungseinschränkungen. Android schränkt ab API 28 (Pie) setAccessible für Non-SDK-Schnittstellen ein — der Versuch, eine versteckte Systemmethode zu öffnen, führt zu einer Ausnahme oder Warnung. iOS mit Swift unterstützt keine Reflection im klassischen Sinne: Die Swift Mirror API bietet nur das Lesen von Eigenschaften (name, value), ohne Änderung oder Methodenaufrufe.
Google Play lehnt Anwendungen ab, die Reflection zur Umgehung von Plattformbeschränkungen verwenden: das Ersetzen von Systemdiensten, das Ändern von SELinux-Richtlinien, das Lesen geschützter Berechtigungen. Apple blockiert ebenfalls Anwendungen, die private APIs über Reflection aufrufen — die App-Review-Prüfung scannt die Binärdatei auf String-Signaturen von objc_msgSend mit bekannten privaten Selektoren.
ProGuard/R8 ist eine weitere Einschränkung. Verschleierung und Minifizierung benennen Klassen und Methoden in kurze Namen (a, b, c) um. Wenn der Code Class.forName(“com.example.MyClass”) verwendet, bricht er nach der Verschleierung. Die Lösung sind Keep-Regeln in proguard-rules.pro:
// ProGuard-Keep-Regeln für Reflection
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod
Die -keep-Regeln teilen R8 mit, Klassen, die über Reflection verwendet werden, nicht umzubenennen. Ohne diese Regeln stürzt eine verschleierte App mit ClassNotFoundException ab — die Laufzeitumgebung kann die Klasse nicht über den String-Namen finden, der sich geändert hat.
Häufig gestellte Fragen
Ja, Reflection ist 10–100 Mal langsamer als ein direkter Aufruf. Die Hauptgründe: fehlende JIT-Optimierungen (Inlining, Devirtualisierung), das Verpacken von Parametern und Typprüfungen bei jedem Aufruf. Für Produktionscode wird empfohlen, Reflection durch Codegenerierung über KSP oder Annotation Processing zu ersetzen.
Die Java-Reflexion arbeitet über Class, Method, Field und erfordert setAccessible für private Elemente. Die Kotlin-Reflexion verwendet KClass, KFunction, KProperty und unterstützt sealed class, data class, Koroutinen (suspend-Funktionen) und Null-Safety. Die Kotlin-Reflexion basiert auf der Java-Reflexion, fügt aber eine typsichere API hinzu.
ProGuard/R8-Keep-Regeln für Klassen, Methoden und Felder hinzufügen, die über Reflection verwendet werden. Für jede Class.forName(), getDeclaredMethod(), getDeclaredField() muss eine entsprechende -keep-Direktive vorhanden sein. Tools wie GreenDAO und Room generieren Keep-Regeln automatisch.
Swift hat keine Reflection im vollständigen Sinne. Mirror API (Swift 2+) erlaubt das Lesen der Eigenschaften einer Struktur oder Klasse: Name, Wert, Typ. Das Aufrufen von Methoden, das Ändern von Feldern und das Erstellen von Instanzen nach Typ sind nicht möglich. Dafür wird die Objective-C-Runtime bei der Vererbung von NSObject mit @objc dynamic verwendet.
Gson (JSON-Serialisierung), Retrofit (Erstellung von Interface-Implementierungen über dynamische Proxys), Mockito (Erstellung von Mocks), Koin (Dependency Injection), Room (Prüfung von Entity zur Kompilierzeit über KAPT), Firebase Crashlytics (Stacktrace-Analyse). Die meisten Bibliotheken wechseln zur Codegenerierung mit KSP/KAPT.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch