> Можно ли пометить лямбду как inline в Kotlin (Kotlin, Android)

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

Компании: TrendTech

Стек: Kotlin, Android

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

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

Да, в Kotlin можно пометить лямбду как inline, но не напрямую - модификатор inline применяется к функции, а не к самой лямбде. Когда функция объявлена как inline, все передаваемые в неё лямбды (включая функциональные параметры) также инлайнятся. При этом лямбду можно передать в inline-функцию, и она будет встроена в место вызова, что устраняет overhead от создания анонимного класса и вызова.

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

inline в Kotlin - это модификатор для функций, который указывает компилятору встроить тело функции и её лямбда-параметры в место вызова. Это работает только для функций, а не для лямбд как самостоятельных сущностей. Лямбда сама по себе не может быть помечена inline, но если она передаётся в inline-функцию, она будет инлайнена.

Основные особенности:

  • inline применяется к функции, а не к лямбде.
  • Все лямбда-параметры inline-функции по умолчанию инлайнятся, если они не помечены noinline или crossinline.
  • Инлайнинг позволяет использовать return внутри лямбды (non-local return), что невозможно для обычных лямбд.
  • Инлайнинг уменьшает overhead от создания объектов и вызовов, но увеличивает размер байткода.

На практике

В Android-разработке inline часто используется для функций с лямбда-параметрами, которые вызываются в горячих точках, например, в циклах или при работе с коллекциями. Типичные примеры - let, apply, run, with, also, а также кастомные функции вроде withContext в корутинах (хотя там inline не всегда применим из-за suspend).

Важно помнить:

  • Не стоит делать inline большие функции - это раздует код.
  • inline нельзя использовать для функций, которые вызываются через reflection или хранят лямбду в поле.
  • Для suspend-функций inline работает только с crossinline и noinline в определённых случаях.

Пример кода

inline fun measureTime(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    println("Time: ${System.currentTimeMillis() - start}")
}

fun main() {
    measureTime {
        // этот код будет встроен в место вызова
        Thread.sleep(100)
    }
}

Здесь measureTime - inline-функция, и лямбда block инлайнится. Внутри лямбды можно использовать return:

fun process(list: List<Int>) {
    list.forEach {
        if (it == 0) return // non-local return, работает только из-за inline
        println(it)
    }
}

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

Начни с прямого ответа: "Лямбду нельзя пометить inline напрямую, но можно передать её в inline-функцию". Затем объясни, что inline - это модификатор функции, и покажи разницу между обычной и inline-функцией. Упомяни про noinline и crossinline - это покажет глубину понимания. Приведи пример из практики, например, let или apply. Если спросят про производительность - скажи, что инлайнинг убирает создание анонимного класса и вызов, но увеличивает размер кода.

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

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

  • Понимание базового синтаксиса Kotlin и модификаторов.
  • Знание механизма инлайнинга и его ограничений.
  • Умение объяснить trade-off между производительностью и размером кода.
  • Понимание non-local return и crossinline/noinline.
  • Практический опыт - как inline используется в стандартной библиотеке и Android.

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

  • Утверждение, что лямбду можно пометить inline напрямую - это неверно.
  • Путаница между inline и noinline - noinline запрещает инлайнинг конкретной лямбды, а не наоборот.
  • Игнорирование crossinline - без него нельзя использовать return в лямбде, переданной в inline-функцию, если она вызывается в другом контексте.
  • Применение inline к большим функциям без необходимости - это ухудшает читаемость и раздувает код.
  • Забывают, что inline не работает с suspend-функциями без crossinline и noinline в некоторых случаях.

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

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