Что такое crossinline в Kotlin и зачем он нужен
Что проверяют
Понимаете ли вы, что inline делает с лямбдой и почему из неё вообще можно выйти наружу. Вопрос проверяет знание нелокального возврата, а не заучивание модификатора: без этой картинки ответ разваливается на первом же уточнении.
Как отвечать
Начните с inline. Компилятор подставляет тело функции и тело лямбды в место вызова, поэтому return внутри лямбды выходит из внешней функции, а не только из лямбды. Это и есть нелокальный возврат, ради него конструкции вроде forEach умеют досрочный выход.
Такой возврат возможен, только пока лямбда вызывается прямо в теле inline-функции. Как только её нужно передать дальше (в другую лямбду, в объект, в поток или в колбэк), подстановка на месте невозможна, и компилятор требует пометить параметр как crossinline. Модификатор запрещает нелокальный возврат и разрешает использовать лямбду в таком контексте.
inline fun onUiThread(crossinline block: () -> Unit) {
Handler(Looper.getMainLooper()).post { block() }
}Рядом держите noinline: он вовсе отменяет подстановку для параметра, и лямбда становится обычным объектом, который можно сохранить в поле или вернуть. Итог одной фразой: crossinline это «встраиваем, но выходить наружу нельзя», noinline это «не встраиваем вообще».
Где спотыкаются
Ставят crossinline там, где лямбда вызывается прямо в теле функции. Смысла нет: модификатор ничего не ускоряет и не улучшает читаемость, он снимает ограничение компилятора в конкретной ситуации и заодно отбирает у вызывающего досрочный выход.
Говорят, что crossinline запрещает return вообще. Возврат с меткой из самой лямбды работает как обычно, запрещён именно выход из внешней функции.
Не видят разницы с noinline и подставляют модификаторы наугад. Разница в судьбе лямбды: с crossinline она по-прежнему встраивается, с noinline создаётся объект, а вместе с ним захват переменных и лишняя аллокация в горячем коде.