TL; DR: Wofür ist das reified
gut ?
fun <T> myGenericFun(c: Class<T>)
Im Hauptteil einer generischen Funktion wie myGenericFun
können Sie nicht auf den Typ zugreifen, T
da er nur zur Kompilierungszeit verfügbar ist, aber zur Laufzeit gelöscht wird. Wenn Sie den generischen Typ als normale Klasse im Funktionskörper verwenden möchten, müssen Sie die Klasse daher explizit als Parameter übergeben, wie in gezeigt myGenericFun
.
Wenn Sie jedoch eine inline
Funktion mit einem Reified erstellen T
, kann auf den Typ von T
auch zur Laufzeit zugegriffen werden, sodass Sie die Funktion nicht Class<T>
zusätzlich übergeben müssen. Sie können damit arbeiten, T
als wäre es eine normale Klasse, z. B. möchten Sie möglicherweise überprüfen, ob eine Variable eine Instanz von ist T
, was Sie dann einfach tun können:myVar is T
.
Eine solche inline
Funktion mit reified
Typ T
sieht wie folgt aus:
inline fun <reified T> myGenericFun()
Wie reified
funktioniert
Sie können nur reified
in Kombination mit einer inline
Funktion verwenden . Durch eine solche Funktion kopiert der Compiler den Bytecode der Funktion an jede Stelle, an der die Funktion verwendet wird (die Funktion wird "inline"). Wenn Sie eine Inline-Funktion mit reifiziertem Typ aufrufen, kennt der Compiler den tatsächlichen Typ, der als Typargument verwendet wird, und ändert den generierten Bytecode, um die entsprechende Klasse direkt zu verwenden. Daher werden Aufrufe wie myVar is T
werden myVar is String
(wenn das Typargument wäre String
) im Bytecode und zur Laufzeit.
Beispiel
Schauen wir uns ein Beispiel an, das zeigt, wie hilfreich es sein reified
kann. Wir möchten eine Erweiterungsfunktion für String
aufgerufene erstellen toKotlinObject
, die versucht, eine JSON-Zeichenfolge in ein einfaches Kotlin-Objekt mit einem Typ zu konvertieren, der durch den generischen Typ der Funktion angegeben wird T
. Wir können com.fasterxml.jackson.module.kotlin
dies verwenden und der erste Ansatz ist der folgende:
a) Erster Ansatz ohne reifizierten Typ
fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(this, T::class.java)
}
Die readValue
Methode verwendet einen Typ, den sie analysieren JsonObject
soll. Wenn wir versuchen, den Class
Parameter type zu erhalten T
, beschwert sich der Compiler: "'T' kann nicht als reifizierter Typparameter verwendet werden. Verwenden Sie stattdessen eine Klasse."
b) Problemumgehung mit expliziten Class
Parametern
fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, c.java)
}
Als Problemumgehung kann das Class
of T
zu einem Methodenparameter gemacht werden, der dann als Argument für verwendet wird readValue
. Dies funktioniert und ist ein allgemeines Muster im generischen Java-Code. Es kann wie folgt aufgerufen werden:
data class MyJsonType(val name: String)
val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)
c) Der Kotlin-Weg: reified
Die Verwendung einer inline
Funktion mit reified
Typparameter T
ermöglicht es, die Funktion anders zu implementieren:
inline fun <reified T: Any> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, T::class.java)
}
Es gibt keine Notwendigkeit , das nehmen Class
von T
zusätzlich, T
kann verwendet werden , als ob es sich um eine normale Klasse. Für den Client sieht der Code folgendermaßen aus:
json.toKotlinObject<MyJsonType>()
Wichtiger Hinweis: Arbeiten mit Java
Eine Inline-Funktion mit reified
Typ kann nicht über Java- Code aufgerufen werden.