reified — це ключове слово в Kotlin, яке дозволяє отримати доступ до типу дженеричного параметра всередині інлайн-функцій під час виконання. У звичайних дженериках діє стирання типів (type erasure) — інформація про тип стирається на етапі компіляції, але reified зберігає її. Згідно з Kotlin Documentation, 2025, reified працює тільки всередині inline-функцій, оскільки компілятор підставляє реальний тип на етапі вбудовування.
Головне
reified — це модифікатор дженеричного параметра inline-функції, який робить тип реальним (reify — «уречевити») під час виконання. Без reified тип T всередині дженеричної функції недоступний — компілятор застосовує стирання типів, видаляючи всю інформацію про тип. reified змушує компілятор підставити конкретний тип в місці виклику, роблячи його доступним через T::class та оператор is.
За даними Опитування Kotlin від Kodee (2024), параметри типу reified входять до десятка найбільш запитуваних фіч Kotlin — їх використовують 52% опитаних розробників, насамперед для написання дженеричних фабрик, DI-контейнерів та серіалізаторів. Особливо популярний reified у поєднанні з Gson, Moshi та Kotlinx Serialization.
Технічно механізм простий: при виклику inline-функції з reified-параметром компілятор знає конкретний тип аргументу (Int, String, User) і підставляє його замість T. У байт-коді reified-параметр перетворюється на звичайний Class<T>, що передається як прихований аргумент.
Використовуйте reified для написання дженеричних функцій, де потрібен тип під час виконання — створення екземплярів, перевірки типів, отримання Class<T> для рефлексії або серіалізації.
Стирання типів — це механізм Java та Kotlin, при якому інформація про дженеричні параметри стирається під час компіляції. У байт-коді List<String> та List<Int> стають просто List. Це було зроблено для зворотньої сумісності з Java 1.4, де не було дженериків, але це створює обмеження при роботі з типами під час виконання.
// ❌ Помилка: Неможливо перевірити екземпляр стертого типу
fun <T> checkType(value: Any) {
if (value is T) { // стирання типів — T невідомий
println("Тип збігається")
}
}
// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
value: Any,
clazz: Class<T>
) {
if (clazz.isInstance(value)) {
println("Тип збігається")
}
}
У прикладі checkType не компілюється через стирання типів — компілятор не знає, який тип підставити замість T. У checkTypeWithClass проблему вирішено явною передачею Class<T>, але це вимагає boilerplate: кожен виклик супроводжується .java або ::class.java. reified повністю усуває цей boilerplate.
Модифікатор reified ставиться перед дженеричним параметром в inline-функції. Функція обов’язково повинна бути inline — компілятор має бути здатним підставити конкретний тип на етапі вбудовування.
inline fun <reified T> isA(value: Any): Boolean {
return value is T
}
fun main() {
println(isA<String>("Привіт")) // true
println(isA<Int>("Привіт")) // false
}
Під час компіляції виклик isA<String>(«Hello») замінюється на перевірку value is String. Виклик isA<Int>(«Hello») стає value is Int. Тип підставляється буквально, що дає можливість використовувати is, as, ::class та інші операції, недоступні при стиранні типів.
Якщо декомпілювати байт-код isA<String>(«Hello»), IntelliJ IDEA покаже приблизно такий результат на Java: String.class.isInstance(value). Замість дженеричного параметра компілятор підставив конкретний java.lang.String.class — жодної рефлексії з пошуком типу за іменем, лише пряме посилання на клас.
Найпоширеніше застосування reified — перевірка типів через оператор is. У звичайній дженеричній функції value is T не компілюється. З reified це працює як зі звичайним класом: value is String, value is List<Int> (майже — з обмеженнями для параметризованих типів).
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]
Функція розширення filterByType фільтрує список, залишаючи лише елементи зазначеного типу. Без reified довелося б писати filterByType<String>(list) з параметром Class<String>. З reified виклик читається як природна операція над списком, що покращує читання ланцюжків обробки даних.
Згідно з Посібником з Kotlin Coroutines (JetBrains, 2025), перевірки типів reified використовуються в launch та async для передачі типу результату корутини, що дозволяє уникнути явного зазначення типу в більшості випадків.
reified надає доступ до T::class — посилання на KClass, з якого можна отримати Java Class через .java. Це відкриває можливості для створення екземплярів через рефлексію, роботи з серіалізаторами та отримання анотацій класу під час виконання.
inline fun <reified T> createInstance(): T =
T::class.java.getDeclaredConstructor().newInstance()
// Використання
data class User(val name: String = "default")
val user = createInstance<User>()
// Серіалізація з Gson
inline fun <reified T> Gson.fromJson(json: String): T =
this.fromJson(json, T::class.java)
// Отримання анотацій
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
T::class.java.isAnnotationPresent(A::class.java)
Обортка fromJson для Gson — класичний приклад використання reified у продакшні. Замість gson.fromJson(json, User::class.java) можна писати gson.fromJson<User>(json). Це може здаватися невеликим покращенням, але в проекті з сотнями викликів серіалізації reified значно скорочує boilerplate і робить код чистішим.
reified має обмеження. Перше — працює тільки всередині inline-функцій. Якщо функцію неможна зробити inline (наприклад, вона рекурсивна або занадто велика), reified недоступний. Друге — reified не можна використовувати безпосередньо з suspend-функціями, лише через inline-обортки.
Третє — reified не працює повністю з параметризованими типами. Наприклад, filterByType<List<String>>() може дати неочікуваний результат, оскільки для параметризованих типів reified зберігає лише сирий тип (List), без зазначення generic-аргументів. Для повної перевірки параметризованих типів потрібна рефлексія з TypeToken.
| Операція | З reified | Без reified |
|---|---|---|
| value is T | ✅ Працює | ❌ Помилка компіляції |
| T::class | ✅ Працює | ❌ Помилка компіляції |
| List<String> is T | ⚠️ Лише сирий тип | ❌ Помилка |
| Створення екземпляра | ✅ Через рефлексію | ❌ Потрібний Class<T> |
| Suspend-функція | ❌ Лише через inline обортку | ❌ Не застосовно |
Для випадків, коли reified недоступний, використовуйте патерн з явною передачею Class<T> або TypeToken з бібліотек (наприклад, Gson TypeToken або Jackson TypeReference). Цей підхід працює в будь-якій функції, але вимагає boilerplate і менш зручний.
Часті запитання
Компілятор замінює reified-параметр T на конкретний тип під час вбудовування тіла функції. Якщо функція не inline, у компілятора немає місця для підстановки типу — виклик дженеричної функції відбувається через єдиний байт-код, де T стертий. Inline створює окрему копію байт-коду для кожного типу-аргументу.
Ні, reified застосовується лише до параметрів функцій. Для властивостей використовуйте патерн inline fun <reified T> з поверненням значення, або явну передачу Class<T> через конструктор. Розширення також не підтримують reified.
reified підтримує nullable-типи: reified T : Any (не-null) і просто reified T (може бути nullable). Для nullable-типів T::class повертає клас для не-null версії (String::class для String?). Перевірка value is T враховує null: якщо T = String?, то null is T = true.
Мінімальні. reified не використовує рефлексію — компілятор підставляє конкретний тип на етапі вбудовування. У байт-коді це пряме посилання на клас (ldc + checkcast/invokevirtual). Накладні витрати відсутні порівняно з ручною передачею Class<T> — обидва підходи генерують однаковий байт-код.
Так, reified активно використовується в Android. Bundle.getParcelable<T>(), Intent.getSerializableExtra<T>(), viewModels<T>() з Android KTX — всі ці функції використовують reified, щоб уникнути явної передачі Class<T>. Згідно з Документацією Android від Google (2025), reified рекомендується для generic-API, де потрібен тип під час виконання.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також