reified — słowo kluczowe w języku Kotlin, które pozwala uzyskać dostęp do typu parametru generic wewnątrz inline-funkcji w czasie wykonania. W zwykłych generics działa type erasure — informacja o typie jest usuwana na etapie kompilacji, ale reified ją zachowuje. Według Kotlin Documentation, 2025, reified działa tylko wewnątrz inline-funkcji, ponieważ kompilator podstawia rzeczywisty typ na etapie wstawiania.
Najważniejsze
reified — to modyfikator parametru generic inline-funkcji, który czyni typ rzeczywistym (reify — «uprzytamniać») w runtime. Bez reified typ T wewnątrz funkcji generic jest niedostępny — kompilator stosuje type erasure, usuwając całą informację o typie. reified zmusza kompilator do podstawienia konkretnego typu w miejscu wywołania, czyniąc go dostępnym przez T::class i operator is.
Według Kotlin Survey by Kodee (2024), reified type parameters wchodzą do dziesiątki najbardziej pożądanych funkcji Kotlin — używa ich 52% ankietowanych programistów, głównie do pisania generic-fabryk, kontenerów DI i serializatorów. Szczególnie popularny jest reified w połączeniu z Gson, Moshi i Kotlinx Serialization.
Technicznie mechanizm jest prosty: przy wywołaniu inline-funkcji z reified parametrem kompilator zna konkretny typ argumentu (Int, String, User) i podstawia go zamiast T. W kodzie bajtowym reified-parametr zamienia się w zwykły Class
Używaj reified do pisania funkcji generic, gdzie wymagany jest typ w runtime — tworzenie instancji, sprawdzanie typu, uzyskiwanie Class
Type erasure — mechanizm Java i Kotlin, w którym informacja o parametrach generic jest usuwana podczas kompilacji. W kodzie bajtowym List
// ❌ Error: Cannot check for instance of erased type
fun <T> checkType(value: Any) {
if (value is T) { // type erasure — T is unknown
println("Type matches")
}
}
// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
value: Any,
clazz: Class<T>
) {
if (clazz.isInstance(value)) {
println("Type matches")
}
}
W przykładzie checkType nie kompiluje się z powodu type erasure — kompilator nie wie, jaki typ podstawić zamiast T. W checkTypeWithClass problem został rozwiązany przez jawne przekazanie Class
Modyfikator reified umieszcza się przed parametrem generic w funkcji inline. Funkcja musi być koniecznie inline — kompilator musi mieć możliwość podstawienia konkretnego typu na etapie wstawiania.
inline fun <reified T> isA(value: Any): Boolean {
return value is T
}
fun main() {
println(isA<String>("Hello")) // true
println(isA<Int>("Hello")) // false
}
Podczas kompilacji wywołanie isA
Jeśli zdekompilować kod bajtowy isA
Najczęstsze zastosowanie reified — sprawdzanie typu przez operator is. W zwykłej funkcji generic value is T nie kompiluje się. Z reified działa to jak ze zwykłą klasą: value is String, value is List
inline fun <reified T> List<Any>.filterByType(): List<T> {
return this.filter { it is T }.map { it as T }
}
val mixed = listOf("a", 1, "b", 2)
val strings = mixed.filterByType<String>() // ["a", "b"]
val ints = mixed.filterByType<Int>() // [1, 2]
Funkcja rozszerzająca filterByType filtruje listę, pozostawiając tylko elementy określonego typu. Bez reified trzeba by pisać filterByType
Według danych Kotlin Coroutines Guide (JetBrains, 2025), reified type checks są używane w launch i async do przekazywania typu wyniku coroutine, co pozwala uniknąć jawnego określania typu w większości przypadków.
reified daje dostęp do T::class — referencji do KClass, z której można uzyskać Java Class przez .java. Otwiera to możliwości tworzenia instancji przez refleksję, pracy z serializatorami i uzyskiwania adnotacji klasy podczas wykonania.
inline fun <reified T> createInstance(): T =
T::class.java.getDeclaredConstructor().newInstance()
// Usage
data class User(val name: String = "default")
val user = createInstance<User>()
// Serialization with Gson
inline fun <reified T> Gson.fromJson(json: String): T =
this.fromJson(json, T::class.java)
// Getting annotations
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
T::class.java.isAnnotationPresent(A::class.java)
Opakowanie fromJson dla Gson — klasyczny przykład użycia reified w produkcji. Zamiast gson.fromJson(json, User::class.java) można pisać gson.fromJson
reified ma ograniczenia. Po pierwsze — działa tylko wewnątrz funkcji inline. Jeśli funkcji nie można uczynić inline (na przykład jest rekurencyjna lub zbyt duża), reified jest niedostępne. Po drugie — reified nie może być używane z suspend-funkcjami bezpośrednio, tylko przez opakowania inline.
Po trzecie — reified nie działa w pełni z typami sparametryzowanymi. Na przykład filterByType>() może dać nieoczekiwany wynik, ponieważ dla typów sparametryzowanych reified zachowuje tylko surowy typ (List), bez określenia argumentów generic. Do pełnego sprawdzenia typów sparametryzowanych wymagana jest refleksja z TypeToken.
| Operacja | Z reified | Bez reified |
|---|---|---|
| value is T | ✅ Działa | ❌ Błąd kompilacji |
| T::class | ✅ Działa | ❌ Błąd kompilacji |
| List | ⚠️ Tylko raw type | ❌ Błąd |
| Tworzenie instancji | ✅ Przez refleksję | ❌ Wymagany Class |
| Suspend-funkcja | ❌ Tylko przez opakowanie inline | ❌ Nie dotyczy |
W przypadkach, gdy reified jest niedostępne, używaj wzorca z jawnym przekazaniem Class
Często zadawane pytania
Kompilator zamienia reified-parametr T na konkretny typ podczas wstawiania ciała funkcji. Jeśli funkcja nie jest inline, kompilator nie ma miejsca, gdzie podstawić typ — wywołanie funkcji generic odbywa się przez jednolity kod bajtowy, w którym T jest wymazany. Inline tworzy osobną kopię kodu bajtowego dla każdego typu-argumentu.
Nie, reified ma zastosowanie tylko do parametrów funkcji. Dla właściwości używa się wzorca inline fun
reified obsługuje typy nullable: reified T : Any (non-null) i po prostu reified T (może być nullable). Dla typów nullable T::class zwraca klasę dla wersji non-null (String::class dla String?). Sprawdzenie value is T uwzględnia null: jeśli T = String?, to null is T = true.
Minimalny. reified nie używa refleksji — kompilator podstawia konkretny typ na etapie wstawiania. W kodzie bajtowym jest to bezpośrednie odwołanie do klasy (ldc + checkcast/invokevirtual). Narzut nie występuje w porównaniu z ręcznym przekazaniem Class
Tak, reified jest aktywnie używane w Androidzie. Bundle.getParcelable
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również