Что такое inline-функции?

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

Ключевое слово: inline

inline fun myFunction(action: () -> Unit) {
println("Before")
action()
println("After")
}

При вызове myFunction { println("Inside") } компилятор вставит весь код прямо туда, где был вызов, без создания объекта-лямбды или перехода по стеку.

📌 Зачем нужны inline-функции?

  1. Устранение накладных расходов при передаче лямбд (объектов-функций).

  2. Позволяют использовать return из лямбды как if/else из внешнего кода.

  3. Улучшают производительность в высокочастотных функциях с лямбдами.

  4. Используются в стандартной библиотеке Kotlin (let, run, apply, with, repeat и др.).

🧠 Как работает inline

До inlining:

doSomething {
println("Hello")
}

→ создаётся объект Function, передаётся внутрь doSomething, вызывается изнутри.

После inlining (на уровне байткода):

println("Before")
println("Hello")
println("After")

экономия на вызовах и объектах.

Пример

inline fun measure(block: () -> Unit) {
val start = System.nanoTime()
block()
val end = System.nanoTime()
println("Execution took ${end - start} ns")
}
measure {
println("Running block")
}

🔧 После компиляции будет что-то вроде:

val start = System.nanoTime()
println("Running block")
val end = System.nanoTime()
println("Execution took ${end - start} ns")

🔄 Использование return в inline-функциях

Лямбда внутри inline-функции может использовать return из внешней функции:

inline fun runIf(condition: Boolean, block: () -> Unit) {
if (condition) block()
}
fun test() {
runIf(false) {
println("Before return")
return // работает!  завершает \`test()\`, а не только лямбду
}
println("After runIf")
}

⚠️ Когда не стоит делать inline

  • Для крупных функций: вставка кода может сильно раздуть байткод.

  • Если лямбда не используется — inlining бесполезен.

  • В inline-функции не должно быть рекурсии (компилятор не позволит).

🔐 noinline и crossinline

noinline

По умолчанию все лямбды инлайнится. Если вы хотите передать одну из них дальше (например, в другую функцию), используйте noinline:

inline fun doTwice(block1: () -> Unit, noinline block2: () -> Unit) {
block1()
block2()
}

→ block1 будет встроен, а block2 — нет.

crossinline

Нельзя использовать return внутри inline-лямбды, если она передаётся в другой контекст (например, в Thread, Runnable, coroutine). Тогда — crossinline:

inline fun launch(block: () -> Unit) {
val r = Runnable {
// block() // ошибка: \`return\` нельзя использовать
// решение:
block()
}
Thread(r).start()
}

→ crossinline запрещает non-local return внутри такой лямбды.

Применение в Android и Kotlin DSL

  • inline активно используется в DSL (build.gradle.kts, Anko, Jetpack Compose).

  • Используется для оптимизации кода с лямбдами.

  • В Jetpack Compose почти все модификаторы — inline.

Итого

Особенность Описание
inline Встраивает тело функции в место вызова
--- ---
Зачем? Уменьшить накладные расходы от лямбд, ускорить выполнение
--- ---
noinline Отключает inlining для отдельных параметров
--- ---
crossinline Запрещает return из лямбды, если она передаётся в другой контекст
--- ---
Когда полезен Часто вызываемые функции с лямбдами, DSL, Compose, корутины
--- ---
Когда вреден В больших функциях, при нарушении читаемости, увеличении размера байткода
--- ---

inline — один из мощных инструментов Kotlin, который позволяет писать выразительный и эффективный код без жертв в производительности.