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

В чем разница между noinline и crossinline?

2.4 Senior🔥 72 комментариев
#Kotlin основы#Производительность и оптимизация

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

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

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

Разница между noinline и crossinline в Kotlin

Оба модификатора используются для управления поведением лямбда-выражений, передаваемых в inline-функции, но решают разные задачи.

Основное назначение

  • noinline — запрещает inline-подстановку конкретной лямбды. Лямбда остаётся объектом и может быть передана дальше.
  • crossinline — требует, чтобы лямбда не содержала нелокальных возвратов (return), но при этом всё равно выполняется inline.

Детальное сравнение

noinline — запрет inline-подстановки

Когда вы объявляете параметр лямбды как noinline, компилятор Kotlin не будет встраивать этот конкретный лямбда-выражение в место вызова. Вместо этого лямбда останется обычным объектом.

Когда использовать:

  • Когда нужно передать лямбду в другую функцию, которая ожидает обычный функциональный тип
  • Когда лямбда используется в контексте, где она должна существовать как объект (например, сохраняется в свойстве)
  • Для уменьшения размера сгенерированного байткода, если лямбда слишком большая

Пример:

inline fun processData(
    data: List<Int>,
    onProcess: (Int) -> Unit,
    noinline onComplete: () -> Unit // Эта лямбда НЕ будет встроена
) {
    data.forEach(onProcess)
    saveCompletionCallback(onComplete) // Можно передать дальше
}

fun saveCompletionCallback(callback: () -> Unit) {
    // Сохраняем callback для использования позже
}

Здесь onComplete объявлена как noinline, потому что она передаётся в другую функцию.

crossinline — запрет нелокальных возвратов

Модификатор crossinline гарантирует, что лямбда не содержит нелокальных возвратов (non-local returns), но при этом лямбда всё равно выполняется как inline.

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

Пример без crossinline (ошибка компиляции):

inline fun runInBackground(crossinline task: () -> Unit) {
    val runnable = object : Runnable {
        override fun run() {
            task() // Выполнение в другом контексте
        }
    }
    Thread(runnable).start()
}

fun main() {
    runInBackground {
        println("Выполняем задачу")
        // return // Здесь return был бы неоднозначным - из какой функции возвращаемся?
    }
}

С crossinline:

inline fun runInBackground(crossinline task: () -> Unit) {
    val runnable = object : Runnable {
        override fun run() {
            task() // Теперь безопасно - task не может содержать return
        }
    }
    Thread(runnable).start()
}

Ключевые различия в таблице

Аспектnoinlinecrossinline
Inline-подстановкаЗапрещенаРазрешена (обязательна)
Нелокальные returnНе разрешены (лямбда — объект)Запрещены явно
Передача лямбдыМожно передавать как объектМожно передавать только в inline-контекстах
Использование returnТолько возврат из самой лямбды (return@label)Только возврат из самой лямбды (return@label)
Объект лямбдыСоздаётся как объектНе создаётся (встраивается)

Практические рекомендации

  1. Используйте noinline, когда:

    • Лямбда используется вне inline-контекста
    • Лямбда сохраняется для отложенного выполнения
    • Нужно уменьшить размер генерируемого кода
  2. Используйте crossinline, когда:

    • Лямбда выполняется в другом контексте (через локальный объект или функцию)
    • Нужно предотвратить нелокальные возвраты
    • Лямбда должна быть inline, но её выполнение происходит не напрямую
  3. Важно: Оба модификатора применяются только к параметрам inline-функций. Для обычных функций они не имеют смысла.

Пример совместного использования

inline fun doWork(
    crossinline before: () -> Unit,    // Без return, но inline
    work: () -> Unit,                   // Обычная inline лямбда
    noinline after: () -> Unit          // Не inline, можно передать дальше
) {
    val task = {
        before()
        work()
    }
    
    executeTask(task)    // crossinline лямбда выполняется здесь
    callbackManager.registerCallback(after) // noinline лямбда передаётся
}

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

В чем разница между noinline и crossinline? | PrepBro