Historia pytania:
W JVM w czasie wykonywania nie ma informacji o parametrach ogólnych (type erasure). Kotlin zaproponował mechanizm reified generic-parameterów dla funkcji inline, aby uzyskać dostęp do informacji o typie T w czasie wykonania bez używania takich sztuczek jak przekazywanie Class<T>. To jeden z najpotężniejszych mechanizmów języka Kotlin.
Problem:
Musi być napisana funkcja, która przyjmuje pewną wartość typu T (ogólnego) i w zależności od typu parametru wykonuje różne operacje, bez wyraźnego przekazywania java.lang.Class i bez refleksji w stylu Java. Klasyczne type erasure nie pozwala na poznanie typu T w czasie wykonania.
Rozwiązanie:
W Kotlinie dla funkcji inline można zadeklarować ogólne parametry z modyfikatorem reified, co pozwala "zatrzymać" typ T wewnątrz ciała funkcji, pracować z nim jak z zwykłym typem, wykonywać type-checks oraz tworzyć instancje przez refleksję.
Przykład kodu:
inline fun <reified T> isOfType(value: Any): Boolean { return value is T } isOfType<String>(123) // false isOfType<Int>(123) // true
Kluczowe cechy:
Czy można używać reified poza funkcjami inline?
Nie! Tylko funkcje inline (i inline-leniwe właściwości) mogą mieć reified generic-parameter. Powód — podstawienie kodu w miejscu wywołania, tylko w ten sposób można „podstawić” konkretny typ zamiast T.
Przykład kodu:
// Błąd kompilacji: fun <reified T> errorFun() { } // Poprawna wersja: inline fun <reified T> okFun() { }
Czy można uzyskać Class<T> wewnątrz inline reified-funkcji?
Tak! Wystarczy napisać T::class.java lub T::class. To bardzo wygodne do pisania ogólnych fabryk, parserów i pracy z API refleksyjnym.
Przykład kodu:
inline fun <reified T> printType() { println(T::class.java) } printType<String>() // class java.lang.String
Czy można tworzyć instancje typu T przez konstruktor w reified-funkcji?
Częściowo. Można użyć refleksji, ale bezpośrednio new T() jak w C++ lub C# nie będzie możliwe:
inline fun <reified T : Any> makeInstance(): T? { return T::class.constructors.firstOrNull()?.call() }
Ale to podejście wymaga istnienia konstruktora bez parametrów w T.
Funkcja z reified, wywołująca w kodzie ciężką operację refleksyjną:
inline fun <reified T> foo(): T? = T::class.constructors.firstOrNull()?.call()
Plusy:
Minusy:
Użycie reified do uniwersalnego type-check lub safeCast:
inline fun <reified T> safeCast(value: Any?): T? = value as? T
Plusy:
Minusy: