Почему нельзя работать с generic без inline в Kotlin
Что проверяют
Вопрос сформулирован неточно, и это часть проверки. С обычными дженериками в Kotlin прекрасно работают без inline. Без него недоступен reified, то есть знание конкретного типа в рантайме. Смотрят, поправите ли вы формулировку и понимаете ли стирание типов на JVM.
Как отвечать
На JVM параметры типа стираются: List<String> и List<Int> в байткоде это один и тот же List. Поэтому внутри обычной fun <T> parse(s: String): T нельзя написать ни T::class, ни x is T. Компилятор просто не знает, чем окажется T.
inline копирует тело функции в место вызова, а там тип уже известен статически. reified T подставляется конкретным классом, и проверка становится возможной:
inline fun <reified T> Gson.parse(json: String): T = fromJson(json, T::class.java)
inline fun <reified T : Activity> Context.open() =
startActivity(Intent(this, T::class.java))Без inline тот же результат достигается передачей токена типа параметром: Class<T>, KClass<T>, TypeToken у Retrofit и Gson, serializer() в kotlinx.serialization. Так что «нельзя» тут означает «нельзя без явной передачи типа».
Стоит назвать и цену: тело копируется на каждый вызов и dex растёт, публичная inline-функция не видит приватные члены своего класса, а вызвать reified-функцию из Java нельзя.
Где спотыкаются
Говорят, что без inline вызов дженерик-функции создаёт лишний объект и потому медленный. Накладной расход у дженериков возникает от упаковки примитивов, а inline экономит объект лямбды, то есть речь про разные вещи.
Вешают inline на всё подряд ради скорости. Для функции без лямбда-параметра и без reified компилятор сам выдаёт предупреждение, что выигрыша не будет, а размер кода вырастет.
Не знают про crossinline и noinline. Лямбда внутри inline-функции допускает выход из внешней функции, поэтому если колбэк сохраняется и вызывается позже, компилятор потребует crossinline, и заученный ответ рассыпается на первом же уточнении.