Bisher kennst du beim Schreiben von Code immer den genauen Typ eines Werts — Kunde,
Bestellung, Produkt — und rufst dessen Properties und Methoden direkt auf. Manche Aufgaben
lassen sich damit nicht lösen: Ein Test-Framework, das für jede beliebige Klasse eines
Projekts prüfen soll, welche Funktionen es gibt, kennt diese Klasse beim Schreiben des Frameworks
noch nicht. Ein generisches Logging-Werkzeug, das automatisch alle Felder eines Objekts ausgeben
soll, ebenso wenig.
Reflection ist die Fähigkeit eines Programms, die Struktur seiner eigenen Typen zur Laufzeit
zu untersuchen (zu introspizieren), statt sie beim Kompilieren fest vorzugeben. In Kotlin ist der
Einstiegspunkt dafür die Klasse KClass, die man über die Class-Literal-Syntax ::class bekommt:
class Kunde(val name: String, val email: String)
fun main() {
val kunde = Kunde("Anna Weber", "anna@example.com")
val typ = kunde::class
println(typ.simpleName) // Kunde
println(typ.qualifiedName) // (Paketname).Kunde
}
Ein wichtiger Unterschied: Kunde::class (direkt an der Klasse) und kunde::class (an einer
Instanz) liefern nicht zwangsläufig dasselbe. Bei Vererbung liefert instanz::class immer die
tatsächliche Klasse zur Laufzeit — unabhängig davon, als welcher Typ die Variable deklariert
ist:
open class Mitarbeiter(val name: String)
class Manager(name: String, val teamgroesse: Int) : Mitarbeiter(name)
fun main() {
val chef: Mitarbeiter = Manager("Julia Bauer", 8)
println(chef::class.simpleName) // Manager, nicht Mitarbeiter
}
chef ist deklariert als Mitarbeiter, enthält aber ein Manager-Objekt. Das ist dieselbe
Unterscheidung, die auch hinter Smart Casts mit is steckt — nur bekommst du hier ein
KClass-Objekt, das du weiterreichen und untersuchen kannst, statt nur einen Boolean-Check.
Manche Java-APIs erwarten kein KClass, sondern ein java.lang.Class. Dafür gibt es die
Property .java:
val javaTyp: Class<Kunde> = Kunde::class.java
::class selbst ist ein Sprachfeature und funktioniert immer, ohne zusätzliche Abhängigkeit. Für
alles, was über die reine Referenz hinausgeht — Eigenschaften wie isData oder isFinal, und
erst recht Listen von Properties und Funktionen, die in den nächsten Lektionen folgen — brauchst
du die separate Runtime-Bibliothek kotlin-reflect. Sie ist bewusst nicht Teil der Standard
Library, damit Anwendungen, die keine Reflection nutzen, keine unnötige Größe mitschleppen.
Mit Gradle (Kotlin DSL) bindest du sie so ein:
dependencies {
implementation(kotlin("reflect"))
}
Fehlt sie, wirft dein Programm typischerweise eine KotlinReflectionNotSupportedError, sobald du
mehr als die reine Referenz brauchst. Ab jetzt gilt für den gesamten Kurs: Diese Abhängigkeit muss
im Projekt vorhanden sein.
Ein paar grundlegende Eigenschaften von KClass, die schon in dieser Lektion nützlich sind:
data class Rechnung(val betrag: Double)
open class Basis
println(Rechnung::class.isData) // true
println(Basis::class.isFinal) // true (nicht als open deklariert)
Lies den Code genau durch, bevor du ihn ausführst.
open class Fahrzeug(val kennzeichen: String)
class Lieferwagen(kennzeichen: String, val ladung: Int) : Fahrzeug(kennzeichen)
fun beschreibe(fahrzeug: Fahrzeug) {
println(fahrzeug::class.simpleName)
}
fun main() {
val fuhrpark: List<Fahrzeug> = listOf(
Fahrzeug("B-AB 123"),
Lieferwagen("B-CD 456", 500)
)
fuhrpark.forEach { beschreibe(it) }
}
beschreibe() nimmt ein Fahrzeug entgegen. Gibt sie für beide Listenelemente "Fahrzeug" aus, oder unterscheidet sich die Ausgabe?
Ein Lagerverwaltungssystem soll für ein beliebiges Objekt protokollieren, mit welcher Klasse es es zu tun hat. Schreibe eine Funktion klassenName(objekt: Any): String, die den simpleName der tatsächlichen Klasse von objekt zurückgibt — oder "Unbekannt", falls simpleName null ist (kann bei anonymen Objekten vorkommen). Rufe sie in main() für ein Produkt und ein Ersatzteil auf.
Bevor du einen Hint bekommst: Was hast du schon probiert, und woran hängt es genau?
1. Was liefert instanz::class, wenn instanz vom deklarierten Typ Basisklasse ist, aber tatsächlich eine Unterklasse enthält?
2. Welche zusätzliche Bibliothek braucht man, um die meisten KClass-Features (z. B. memberProperties, die in einer späteren Lektion vorkommen) zur Laufzeit zu nutzen?
3. Was liefert Kunde::class.java?