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

Ответ

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

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

// Вызов
measureTime { 
    // какой-то код 
}

Нюансы:

  • Увеличивают размер билда (код дублируется)
  • Нельзя использовать в рекурсивных функциях
  • noinline — если нужно исключить inline для конкретного лямбда-параметра

Ответ 18+ 🔞

А, ну вот, inline-функции в Kotlin! Ёпта, это же одна из тех фич, про которую все говорят, но не все до конца понимают, нахуй она нужна. Сейчас разжуем, как для дебила.

Представь себе обычную функцию, которая принимает лямбду. Каждый раз, когда ты её вызываешь, под капотом создаётся новый объект для этой лямбды. Это как если бы ты каждый раз, чтобы позвать соседа за солью, строил ему новый балкон. Трата ресурсов — овердохуища! Вот именно от этой ерунды и спасает ключевое слово inline.

Когда ты пишешь inline перед функцией, ты как бы говоришь компилятору: «Слушай, дружище, не городи тут огород. Возьми код изнутри этой функции и просто вставь его на место вызова, как будто я его там и написал». Всё. Никаких лишних объектов, никаких накладных расходов на вызов. Особенно это кайфово для маленьких утилитных функций, которые ты вызываешь в циклах или в критичных по производительности местах.

Вот смотри на этот пример, тут всё понятно как божий день:

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

// Вызов
measureTime { 
    // какой-то код 
}

Компилятор возьмёт и развернёт это примерно вот так:

val start = System.currentTimeMillis()
// твой "какой-то код" окажется прямо тут
println("Time: ${System.currentTimeMillis() - start}ms")

Красота, да? Всё прямо, без посредников. Но, как и всё в этом мире, у этой магии есть свои подводные грабли, о которые можно запросто расшибить лоб.

Нюансы, про которые надо помнить, а то будет тебе хиросима:

  • Размер билда увеличивается. Ну, логично же, ёпта! Если ты вставил один и тот же кусок кода в сто разных мест, вместо одного вызова функции, то места он займёт в сто раз больше. Это как вместо одной инструкции «позвони Васе» развесить по всему городу плакаты с его номером. Используй inline с умом, только там, где это реально даёт профит, а не для каждой функции подряд.
  • В рекурсивные функции это говно не засунешь. Компилятор просто офигеет и скажет тебе «иди ты нахуй», потому что он не может развернуть бесконечную вложенность. Представь, что пытаешься вставить функцию внутрь себя же — это пиздец, мрак, конец света.
  • Волшебное слово noinline. А что, если у тебя в inline-функции два лямбда-параметра, а инлайнить нужно только один? Например, один ты передашь дальше в другую не-inline функцию. Вот тут и выруливает noinline. Ставишь его перед параметром — и компилятор понимает: «окей, этого чувака не трогаем, пусть остаётся как есть».
inline fun doSomething(inlineBlock: () -> Unit, noinline regularBlock: () -> Unit) {
    inlineBlock() // Этот развернётся
    someOtherFunction(regularBlock) // А этот передастся как есть
}

Короче, инструмент мощный, но не игрушечный. Если юзать его с толком — производительность подрастёт. Если тыкать везде бездумно — получишь раздутую апку и удивление пиздец на лице. Думай головой, блядь!