> Как проверить тип параметра в обобщённом методе Kotlin (Kotlin, Android)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Aston

Стек: Kotlin, Android

> Пример ответа

Короткий ответ

В Kotlin тип параметра обобщённого метода проверяется через is-проверку, но с ограничением: из-за type erasure на JVM нельзя напрямую проверить конкретный тип аргумента внутри generic-метода. Решение - использовать reified с inline, что позволяет сохранить информацию о типе в рантайме. Альтернатива - передавать Class<T> как параметр. Для Android-контекста также часто используют KClass или проверку через when с приведением типов.

Подробное объяснение

В Kotlin, как и в Java, дженерики стираются на уровне JVM. Это значит, что внутри обычного обобщённого метода fun <T> check(value: T) компилятор не знает фактический тип T в рантайме. Поэтому конструкция value is T не скомпилируется - компилятор выдаст ошибку "cannot check for instance of erased type".

Решение - inline + reified. Ключевое слово reified позволяет использовать тип T как обычный класс внутри inline-функции. Компилятор подставляет фактический тип в место вызова, и проверка value is T становится возможной.

Важно понимать ограничения:

  • reified работает только с inline-функциями.
  • Нельзя использовать reified в open-методах или методах, которые переопределяются.
  • reified не работает с примитивами напрямую (но Kotlin автоматически оборачивает их в обёртки).
  • Проверка is с reified работает корректно только для конкретных типов, не для generic-параметров других функций.

Если inline нежелателен (например, из-за размера кода), передают Class<T> или KClass<T> как параметр и используют isInstance или isAssignableFrom.

На практике

В Android-разработке reified часто используют для упрощения работы с Gson, Moshi, Retrofit, ViewModel и SharedPreferences. Например, типичная функция для чтения из SharedPreferences с приведением типа. Также reified удобен для создания экземпляров через рефлексию, например, для ViewModelProvider.

На собеседовании важно показать понимание trade-off: inline увеличивает размер байткода, но даёт возможность работать с типом в рантайме. Также стоит упомянуть, что reified - это compile-time подстановка, а не магия в рантайме.

Пример кода

// Проверка типа с reified
inline fun <reified T> checkType(value: Any): Boolean {
    return value is T
}

// Использование
val result = checkType<String>("hello") // true
val result2 = checkType<Int>("hello")   // false

// Альтернатива без reified
fun <T> checkTypeWithClass(value: Any, clazz: Class<T>): Boolean {
    return clazz.isInstance(value)
}

// Пример для Android: чтение из SharedPreferences
inline fun <reified T> SharedPreferences.getPref(key: String, default: T): T {
    val value = when (T::class) {
        String::class -> getString(key, default as String)
        Int::class -> getInt(key, default as Int)
        Boolean::class -> getBoolean(key, default as Boolean)
        else -> throw IllegalArgumentException("Unsupported type")
    }
    return value as T
}

Как отвечать на собеседовании

Начни с проблемы: type erasure в JVM. Затем объясни два подхода: reified с inline и передача Class. Подчеркни, что reified - это синтаксический сахар, который подставляет тип на этапе компиляции. Приведи пример из Android-практики, например, работу с SharedPreferences или Gson. Упомяни ограничения: нельзя использовать в open-функциях, увеличивается размер кода. Если спросят про производительность - скажи, что inline может даже улучшить её за счёт устранения вызова функции.

Что проверяет интервьюер

Интервьюер проверяет:

  • понимание дженериков и type erasure;
  • знание ключевого слова reified и его ограничений;
  • умение применять это в реальных Android-сценариях;
  • осознание trade-off между inline и передачей Class;
  • способность объяснить разницу между compile-time и runtime.

Типичные ошибки

  • Попытка использовать is T без reified - ошибка компиляции.
  • Забывают, что reified требует inline.
  • Используют reified в open-методах или интерфейсах - не скомпилируется.
  • Путают is с as - as не проверяет тип, а приводит, может бросить исключение.
  • Не учитывают, что reified работает только с конкретными типами, не с generic-параметрами других функций.
  • В Android-контексте забывают про стирание типов в Gson - TypeToken нужен именно из-за этого.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью