История вопроса:
С ростом популярности функционального программирования и лямбда-выражений в JVM возникла проблема накладных расходов на анонимные объекты и дополнительные вызовы методов. В Java подобное встречается с использованием функциональных интерфейсов. В Kotlin ввели ключевое слово inline, чтобы избежать лишних аллокаций при передаче лямбда-функций.
Проблема:
Вызов функции с лямбда-параметрами создает анонимные объекты на куче и увеличивает глубину стека, что замедляет выполнение кода, особенно при активном использовании в циклах и вложенных вызовах.
Решение:
Kotlin позволяет объявить функцию с модификатором inline, после чего тело функции и переданные ей лямбды подставляются прямо в место вызова при компиляции. Это позволяет компилятору избавиться от дополнительных аллокаций и повысить производительность, особенно для коротких, часто вызываемых функций (например, фильтрация коллекций).
Пример кода:
inline fun <T> Iterable<T>.myFilter(predicate: (T) -> Boolean): List<T> { val result = mutableListOf<T>() for (item in this) if (predicate(item)) result.add(item) return result } val filtered = listOf(1, 2, 3, 4).myFilter { it % 2 == 0 } println(filtered) // [2, 4]
Ключевые особенности:
noinline и crossinline для контроля inlining'а отдельных лямбд.Можно ли использовать inline функцию с любым функциональным параметром или generic-типа T?
Нет, inline-функции в первую очередь экономят на call overhead у лямб-параметров, но сам generic-параметр (например, T) не инлайнится — для этого нужен reified modifier. Для простых T без reified, информация о типе будет стёрта на этапе компиляции.
Что произойдет, если в inline-функции объявить замыкание на переменную из внешнего скоупа?
Переменные из внешнего скоупа копируются внутрь инлайненного выражения. Все обращения к ним будут работать так, словно код реально подставлен. Это может привести к неожиданному поведению, если вы не ожидаете побочных эффектов.
Можно ли вызывать inline функцию из Java-кода?
Да, но она будет скомпилирована как обычная функция, и Java-код не увидит никаких преимуществ inlining'а. Kotlin добивается оптимизации только при использовании inline-функций из Kotlin-кода.
return в inline-лямбдах — некорректный контроль потокаРазработчик инлайнит длинную функцию с несколькими лямбдами фильтрации и постобработки, рассчитывая на ускорение. Код компилируется долго, итоговый APK/RAR/DEX значительно увеличивается, при этом прироста в скорости нет.
Плюсы:
Минусы:
Реализована небольшая inline-функция для коротких фильтров массива, вызываемая сотни раз в горячем цикле — время выполнения критически важно. Количество выделений памяти минимально, так как лямбда не создает анонимный объект.
Плюсы:
Минусы: