Geschichte der Frage:
In der JVM fehlen zur Laufzeit Informationen über Generikaparams (Type Erasure). Kotlin hat einen Mechanismus für reified Generic-Parameter für Inline-Funktionen vorgeschlagen, um während der Ausführung ohne Workarounds wie die Übergabe von Class<T> Zugriff auf Informationen über den Typ T zu ermöglichen. Dies ist eines der mächtigsten Mittel der Sprache Kotlin.
Problem:
Es ist erforderlich, eine Funktion zu schreiben, die einen Wert vom Typ T (generic) annimmt und je nach Typ des Parameters unterschiedliche Aktionen ausführt, ohne explizit java.lang.Class zu übergeben und ohne Reflexion auf Java-Ebene. Klassische Type Erasure verhindert das Erkennen des Typs T zur Laufzeit.
Lösung:
In Kotlin darf man für Inline-Funktionen Generic-Parameter mit dem Modifikator reified deklarieren, was es ermöglicht, den Typ T innerhalb des Funktionskörpers zu "erfassen", mit ihm wie mit einem normalen Typ zu arbeiten, Typprüfungen durchzuführen und Instanzen über Reflexion zu erstellen.
Beispielcode:
inline fun <reified T> isOfType(value: Any): Boolean { return value is T } isOfType<String>(123) // false isOfType<Int>(123) // true
Wichtige Merkmale:
Kann man reified außerhalb von Inline-Funktionen verwenden?
Nein! Nur Inline-Funktionen (und inline-lazy properties) können einen reified Generic-Parameter haben. Der Grund ist die Code-Einfügung an der Stelle des Aufrufs, nur so kann ein koncreter Typ anstelle von T "eingesetzt" werden.
Beispielcode:
// Kompilierungsfehler: fun <reified T> errorFun() { } // Gültige Option: inline fun <reified T> okFun() { }
Kann man Class<T> innerhalb einer inline reified-Funktion erhalten?
Ja! Man muss lediglich T::class.java oder T::class schreiben. Dies ist äußerst praktisch für das Schreiben von generischen Fabriken, Parsern und für die Arbeit mit der Reflexions-API.
Beispielcode:
inline fun <reified T> printType() { println(T::class.java) } printType<String>() // class java.lang.String
Kann man Instanzen des Typs T über den Konstruktor in einer reified-Funktion erstellen?
Teilweise. Man kann Reflexion verwenden, aber direkt new T() wie in C++ oder C# wird nicht möglich sein:
inline fun <reified T : Any> makeInstance(): T? { return T::class.constructors.firstOrNull()?.call() }
Dieser Ansatz erfordert jedoch, dass T einen parameterlosen Konstruktor hat.
Eine Funktion mit reified, die eine aufwendige Reflexionsoperation im Code aufruft:
inline fun <reified T> foo(): T? = T::class.constructors.firstOrNull()?.call()
Vorteile:
Nachteile:
Verwendung von reified für einen universellen type-check oder safeCast:
inline fun <reified T> safeCast(value: Any?): T? = value as? T
Vorteile:
Nachteile: