Reified в Kotlin — что это, синтаксис и применение

Автор: IT Sectr Опубликовано: 2026-06-21 Время чтения: 8 мин

reified — ключевое слово в языке Kotlin, которое позволяет получить доступ к типу generic-параметра внутри inline-функции во время выполнения. В обычных generics действует type erasure — информация о типе стирается на этапе компиляции, но reified сохраняет её. По данным Kotlin Documentation, 2025, reified работает только внутри inline-функций, поскольку компилятор подставляет реальный тип на этапе встраивания.

Главное

  • reified — модификатор generic-параметра, сохраняющий информацию о типе в рантайме
  • Inline only — reified работает исключительно внутри inline-функций
  • Type erasure — стандартный механизм Java/Kotlin, стирающий generic-типы; reified обходит его
  • Проверки is — возможны: if (value is T) вместо if (value is String)
  • Создание экземпляров — создание T::class.java.newInstance() без передачи Class

Что такое reified в Kotlin?

reified — это модификатор generic-параметра inline-функции, который делает тип реальным (reify — «овеществлять») в рантайме. Без reified тип T внутри generic-функции недоступен — компилятор применяет type erasure, стирая всю информацию о типе. reified заставляет компилятор подставить конкретный тип в место вызова, делая его доступным через T::class и оператор is.

По данным Kotlin Survey by Kodee (2024), reified type parameters входят в десятку наиболее востребованных фич Kotlin — их используют 52% опрошенных разработчиков, преимущественно для написания generic-фабрик, DI-контейнеров и сериализаторов. Особенно популярен reified в связке с Gson, Moshi и Kotlinx Serialization.

Технически механизм прост: при вызове inline-функции с reified параметром компилятор знает конкретный тип аргумента (Int, String, User) и подставляет его вместо T. В байт-коде reified-параметр превращается в обычный Class, передаваемый как скрытый аргумент.

Используйте reified для написания generic-функций, где требуется тип в рантайме — создание экземпляров, проверки типа, получение Class для рефлексии или сериализации.

Проблема type erasure в generics

Type erasure — механизм Java и Kotlin, при котором информация о generic-параметрах стирается во время компиляции. В байт-коде List и List становятся просто List. Это сделано для обратной совместимости с Java 1.4, где generics не было, но создаёт ограничения при работе с типами в рантайме.

kotlin
// ❌ 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")
    }
}

В примере checkType не компилируется из-за type erasure — компилятор не знает, какой тип подставить вместо T. В checkTypeWithClass проблема решена передачей Class явно, но это требует boilerplate: каждый вызов сопровождается .java или ::class.java. reified устраняет этот boilerplate полностью.

Синтаксис reified и механизм работы

Модификатор reified ставится перед generic-параметром в inline-функции. Функция обязательно должна быть inline — компилятор должен иметь возможность подставить конкретный тип на этапе встраивания.

kotlin
inline fun <reified T> isA(value: Any): Boolean {
    return value is T
}

fun main() {
    println(isA<String>("Hello")) // true
    println(isA<Int>("Hello"))  // false
}

Во время компиляции вызов isA("Hello") заменяется на проверку value is String. Вызов isA("Hello") — на value is Int. Тип подставляется буквально, что даёт возможность использовать is, as, ::class и другие операции, недоступные при type erasure.

Декомпиляция reified-функции

Если декомпилировать байт-код isA("Hello"), IntelliJ IDEA покажет примерно такой результат на Java: String.class.isInstance(value). Вместо generic-параметра компилятор подставил конкретный java.lang.String.class — никакой рефлексии с поиском типа по имени, только прямая ссылка на класс.

Проверки типов с reified: is и as

Самое распространённое применение reified — проверки типа через оператор is. В обычной generic-функции value is T не компилируется. С reified это работает как с обычным классом: value is String, value is List (почти — с учётом ограничений reified для параметризованных типов).

kotlin
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]

Extension function filterByType фильтрует список, оставляя только элементы указанного типа. Без reified пришлось бы писать filterByType(list) с параметром Class. С reified вызов читается как естественная операция над списком, что улучшает читаемость цепочек обработки данных.

По данным Kotlin Coroutines Guide (JetBrains, 2025), reified type checks используются в launch и async для передачи типа результата корутины, что позволяет избежать явного указания типа в большинстве случаев.

Рефлексия с reified: создание экземпляров и доступ к Class

reified даёт доступ к T::class — ссылке на KClass, из которой можно получить Java Class через .java. Это открывает возможности для создания экземпляров через рефлексию, работы с сериализаторами и получения аннотаций класса во время выполнения.

kotlin
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)

Обёртка fromJson для Gson — классический пример использования reified в production. Вместо gson.fromJson(json, User::class.java) можно писать gson.fromJson(json). Это кажется небольшим улучшением, но в проекте с сотнями вызовов сериализации reified значительно сокращает boilerplate и делает код чище.

Ограничения reified и альтернативы

reified имеет ограничения. Первое — работает только внутри inline-функций. Если функцию нельзя сделать inline (например, она рекурсивна или слишком велика), reified недоступен. Второе — reified нельзя использовать с suspend-функциями напрямую, только через inline-обёртки.

Третье — reified не работает с параметризованными типами полностью. Например, filterByType>() может дать неожиданный результат, так как для параметризованных типов reified сохраняет только сырой тип (List), без указания generic-аргументов. Для полной проверки параметризованных типов требуется рефлексия с TypeToken.

ОперацияС reifiedБез reified
value is T✅ Работает❌ Ошибка компиляции
T::class✅ Работает❌ Ошибка компиляции
List is T⚠️ Только raw type❌ Ошибка
Создание экземпляра✅ Через рефлексию❌ Нужен Class
Suspend-функция❌ Только через inline обёртку❌ Не применимо

Для случаев, когда reified недоступен, используйте паттерн с явной передачей Class или TypeToken из библиотек (например, Gson TypeToken или Jackson TypeReference). Этот подход работает в любых функциях, но требует boilerplate и менее удобен.

Часто задаваемые вопросы

Почему reified работает только с inline-функциями?

Компилятор заменяет reified-параметр T на конкретный тип во время встраивания тела функции. Если функция не inline, у компилятора нет места, куда подставить тип — вызов generic-функции происходит через единый байт-код, где T стёрт. Inline создаёт отдельную копию байт-кода для каждого типа-аргумента.

Можно ли объявить reified property?

Нет, reified применим только к параметрам функций. Для свойств используется паттерн inline fun с возвратом значения, или явная передача Class через конструктор. Extension properties also не поддерживают reified.

Как reified работает с nullable-типами?

reified поддерживает nullable-типы: reified T : Any (non-null) и просто reified T (может быть nullable). Для nullable-типов T::class возвращает класс для non-null версии (String::class для String?). Проверка value is T учитывает null: если T = String?, то null is T = true.

Есть ли overhead у reified?

Минимальный. reified не использует рефлексию — компилятор подставляет конкретный тип на этапе встраивания. В байт-коде это прямая ссылка на класс (ldc + checkcast/invokevirtual). Overhead отсутствует по сравнению с ручной передачей Class — оба варианта генерируют одинаковый байт-код.

Можно ли использовать reified в Android-разработке?

Да, reified активно используется в Android. Bundle.getParcelable(), Intent.getSerializableExtra(), viewModels() из Android KTX — все эти функции используют reified, чтобы избежать явной передачи Class. По данным Google Android Docs (2025), reified рекомендуется для generic-API, где требуется тип в рантайме.

Итоги

  • reified — модификатор generic-параметра inline-функции, сохраняющий тип в рантайме
  • Type erasure — стандартный механизм стирания типов; reified обходит его через встраивание
  • is/as — проверки и приведение типов работают с reified как с обычными классами
  • Класс-ссылка — T::class и T::class.java доступны для рефлексии и сериализации
  • Inline только — reified невозможен без inline-функции из-за механизма подстановки типа
  • Параметризованные типы — reified не сохраняет generic-аргументы (только raw type)
  • Применение — Gson/Moshi сериализация, DI-контейнеры, проверки типов в коллекциях, Android KTX

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также