← Назад к вопросам

Что такое crossinline функция в Kotlin?

2.2 Middle🔥 201 комментариев
#Kotlin основы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое crossinline в Kotlin?

Crossinline — это модификатор параметра функции в Kotlin, который указывает, что лямбда-выражение, переданное в этот параметр, может быть выполнено не напрямую (например, из другого контекста выполнения), но при этом не должно содержать нелокальных возвратов (return без метки). Это гарантия компилятору, что лямбда не будет использована для нелокального управления потоком выполнения.

Основной контекст использования

crossinline применяется в основном к параметрам функций, помеченным как inline (встраиваемые функции). Встраивание позволяет компилятору подставлять тело функции и переданные лямбды прямо в место вызова, что устраняет накладные расходы на вызов функции и позволяет использовать нелокальные возвраты. Однако, если лямбда будет выполнена не в момент вызова inline-функции (например, сохранена или передана в другой контекст), нелокальный return становится неоднозначным. Вот здесь и нужен crossinline.

Пример проблемы без crossinline

Рассмотрим ситуацию, где inline-функция передает лямбду дальше:

inline fun executeLater(block: () -> Unit) {
    // ПЕРЕДАЧА ЛЯМБДЫ В ДРУГОЙ КОНТЕКСТ ВЫПОЛНЕНИЯ
    Runnable { block() }.run() // Ошибка компиляции без crossinline!
}

Здесь block выполняется внутри Runnable, который может быть запущен позже. Если в block был бы нелокальный return, компилятор не смог бы определить, из какой функции он должен вернуться. Это приводит к ошибке компиляции.

Решение с crossinline

Добавим crossinline к параметру:

inline fun executeLater(crossinline block: () -> Unit) {
    // Теперь можно передать лямбду в другой контекст
    Runnable { block() }.run() // Компилируется успешно
}

fun main() {
    executeLater {
        println("Выполняется в Runnable")
        // return // Такой return вызовет ошибку компиляции: "Can't inline 'block'..."
    }
}

Ключевое изменение: внутри executeLater теперь можно использовать block в лямбдах, которые выполняются не сразу, но запрещены нелокальные возвраты из block.

Сравнение с обычными inline-параметрами

АспектОбычный inline-параметр (без crossinline)Параметр с crossinline
Нелокальный returnРазрешен (return возвращает из внешней функции)Запрещен (только локальные возвраты или завершение лямбды)
Передача лямбдыТолько прямое выполнение в теле inline-функцииМожно передавать во вложенные лямбды или объекты
ПроизводительностьПолное встраивание, нулевые накладные расходыЛямбда все еще встраивается, но с ограничениями

Практический пример из Android

В Android crossinline часто встречается в кастомных inline-функциях, работающих с асинхронными вызовами:

inline fun View.onClickCrossInline(
    crossinline onClick: (View) -> Unit
) {
    setOnClickListener { view ->
        onClick(view) // Лямбда выполняется внутри другой лямбды
    }
}

// Использование
button.onClickCrossInline {
    Toast.makeText(context, "Клик!", Toast.LENGTH_SHORT).show()
    // return нельзя, так как это нелокальный возврат из лямбды
}

Важные технические детали

  1. Только для inline-функций: crossinline можно применять только к параметрам функций, объявленных как inline.
  2. Локальные возвраты разрешены: Внутри лямбды с crossinline можно использовать return@label для возврата только из самой лямбды.
  3. Нет боксования: Как и в обычных inline-функциях, лямбда с crossinline не создает объектов функциональных интерфейсов, что сохраняет производительность.

Пример с локальным возвратом

inline fun processList(
    crossinline processor: (Int) -> Unit
) {
    listOf(1, 2, 3).forEach {
        if (it == 2) return@forEach // Локальный возврат разрешен
        processor(it)
    }
}

Выводы

  • Crossinline — это механизм безопасности, который позволяет компилятору встраивать лямбды даже при их передаче в другие контексты выполнения, но гарантирует отсутствие нелокальных возвратов.
  • Используется в inline-функциях, где лямбда-параметр выполняется не напрямую (например, внутри другой лямбды, объекта или асинхронно).
  • Улучшает производительность (избегает создания анонимных классов) при сохранении контроля за потоком выполнения.
  • Критически важен для библиотек и DSL, где нужна гибкость в использовании лямбд без неожиданных нелокальных переходов.

Понимание crossinline необходимо для написания эффективных и безопасных inline-функций в Kotlin, особенно при работе с асинхронными операциями или кастомными DSL.