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

Когда нужно использовать inline?

2.0 Middle🔥 111 комментариев
#Kotlin основы#Производительность и оптимизация

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

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

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

Основные сценарии использования inline в Kotlin

inline в Kotlin — это ключевое слово, которое указывает компилятору подставить тело функции (или лямбды) непосредственно в место вызова, вместо генерации обычного вызова функции. Это позволяет избежать накладных расходов на вызов и открывает дополнительные возможности для работы с reified type parameters и non-local returns.

1. Оптимизация производительности для функций высшего порядка

Основное применение — уменьшение overhead при передаче лямбда-выражений. Без inline лямбда компилируется в объект (FunctionN), что создает дополнительную аллокацию и вызов виртуального метода. inline устраняет это, встраивая код лямбды напрямую.

Пример без inline:

fun withoutInline(block: () -> Unit) {
    println("Before")
    block() // Вызов через объект Function0
    println("After")
}

fun test() {
    withoutInline { println("Lambda") }
}
// При компиляции создается анонимный класс для лямбды

Пример с inline:

inline fun withInline(block: () -> Unit) {
    println("Before")
    block() // Код будет встроен
    println("After")
}

fun test() {
    withInline { println("Lambda") }
}
// После компиляции эквивалентно:
// println("Before")
// println("Lambda")
// println("After")
// Никаких объектов не создается

2. Использование reified type parameters

Только inline-функции могут иметь reified type parameters (овеществляемые параметры типа), которые сохраняют информацию о типе во время выполнения. Это полезно для работы с рефлексией без явной передачи Class<T>.

inline fun <reified T> checkType(value: Any): Boolean {
    return value is T // Можно проверить тип, т.к. T известен в runtime
}

inline fun <reified T> parseJson(json: String): T {
    val typeToken = object : TypeToken<T>() {}.type // Пример с Gson
    return Gson().fromJson(json, typeToken)
}

3. Non-local returns

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

inline fun forEachInline(items: List<Int>, action: (Int) -> Unit) {
    for (item in items) action(item)
}

fun findValue(values: List<Int>, target: Int) {
    forEachInline(values) {
        if (it == target) {
            println("Found")
            return // Выход из findValue, а не только из лямбды!
        }
    }
}

Когда НЕ следует использовать inline:

  1. Большие функции — встраивание увеличит размер бинарного кода (code bloat).
  2. Функции без параметров-лямбд — выгода минимальна, если нет лямбд для оптимизации.
  3. Рекурсивные функции — не могут быть встроены (прямо или косвенно).
  4. Public API в библиотеках — изменение inline-функции может потребовать перекомпиляции клиентского кода из-за изменений в бинарном ABI.

Особенности с crossinline и noinline

  • noinline — отменяет встраивание для конкретного параметра-лямбды, если нужно передать ее дальше как объект.
  • crossinline — гарантирует, что лямбда не содержит non-local returns, но все равно будет встроена.
inline fun process(
    crossinline before: () -> Unit, // Без non-local return
    noinline after: () -> Unit      // Не встраивать, сохранить как объект
): () -> Unit {
    before()
    // after можно сохранить или передать
    return after 
}

Вывод: Используйте inline преимущественно для небольших функций высшего порядка, где важна производительность, либо когда нужны reified generics или non-local returns. Для простых функций без лямбд или больших методов inline может принести больше вреда, чем пользы.