Как проверить тип параметра в обобщённом методе Kotlin
Что проверяют
Знаете ли вы про стирание типов и чем в Kotlin его обходят. Код с when (value) { is String -> ... } пишет каждый, а интервьюер ждёт вопроса «а как проверить, что value имеет тип T?». Следом обычно спрашивают, почему reified работает только в inline-функциях.
Как отвечать
Начните с ограничения: на JVM параметр T после компиляции стирается, поэтому запись value is T в обычной обобщённой функции не компилируется, ошибка «Cannot check for instance of erased type». Дальше три рабочих способа:
- проверка на конкретные типы:
is String,is Intвнутриwhenработают, потому что сравнение идёт с известным классом. Послеisсрабатывает smart cast, приводить вручную не нужно; inline fun <reified T>: компилятор подставляет тело функции в место вызова вместе с настоящим типом, и становятся доступныvalue is T,T::classиtypeOf<T>(). Так устроеныfilterIsInstance<T>()иby viewModels<T>();- передача класса явно: параметр
KClass<T>илиClass<T>и проверкаklass.isInstance(value). Вариант нужен, когда функция не может бытьinlineили её зовут из Java. На Android по той же схеме с API 33 работаетBundle.getParcelable(key, Class), а для старых версий естьBundleCompat.
Отдельно упомяните обобщённые коллекции: даже с reified проверить is List<String> нельзя, только is List<*>, элементы придётся проверять по одному.
Где спотыкаются
Пишут value as T и считают это проверкой. Компилятор выдаёт предупреждение о непроверяемом приведении, в рантайме проверка не выполняется, и ClassCastException вылетает позже, в месте использования.
Забывают про nullable: у T без ограничений верхняя граница Any?, поэтому value::class не скомпилируется. Нужна граница T : Any или проверка на null.
Предлагают reified везде. Такая функция встраивается в каждое место вызова, раздувает байткод и недоступна из Java, так что для больших функций выгоднее передать KClass.