Можно ли делать каждую функцию inline?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Можно ли делать каждую функцию inline?
Короткий ответ
Нет, не нужно. Inline функции имеют свои trade-offs, и их следует использовать избирательно для конкретных случаев.
Что такое inline функции
Inline функция — это функция, код которой копируется (inlining) в место вызова во время компиляции, вместо того чтобы создавать вызов функции на runtime.
// Обычная функция
fun add(a: Int, b: Int): Int {
return a + b
}
val result = add(1, 2) // Вызов функции
// Inline функция
inline fun addInline(a: Int, b: Int): Int {
return a + b
}
val result = addInline(1, 2) // Код подставляется прямо сюда
// После компиляции (inline):
val result = 1 + 2 // Нет вызова функции!
Преимущества inline функций
1. Производительность
Исключается overhead вызова функции (создание stack frame, переход по адресу).
// Без inline — много вызовов в цикле
for (i in 1..1000000) {
val sum = add(i, 1) // 1 млн вызовов
}
// С inline — прямое добавление, быстрее
inline fun add(a: Int, b: Int) = a + b
for (i in 1..1000000) {
val sum = add(i, 1) // Код подставляется, нет вызовов
}
2. Lambda без allocation
Самое важное применение inline — это передача lambda без создания объекта.
// БЕЗ inline — lambda оборачивается в объект Function
fun forEach(action: (Int) -> Unit) {
for (i in 1..10) {
action(i) // Вызов через Function объект
}
}
listOf(1, 2, 3).forEach { println(it) } // Создаёт Function объект
// С inline — lambda код подставляется прямо
inline fun forEach(action: (Int) -> Unit) {
for (i in 1..10) {
action(i) // Код lambda подставляется сюда
}
}
listOf(1, 2, 3).forEach { println(it) } // БЕЗ allocation!
Это особенно важно для часто вызываемых функций (map, filter, forEach и т.д.).
Недостатки inline функций
1. Увеличение размера бинарника (APK/JAR)
Каждый вызов inline функции копирует её код, что увеличивает размер:
inline fun add(a: Int, b: Int): Int = a + b
// Если функцию вызвать 100 раз
val r1 = add(1, 2)
val r2 = add(2, 3)
val r3 = add(3, 4)
// ...
val r100 = add(99, 100)
// Компилятор скопирует код 100 раз (увеличит размер)
Для мобильных приложений это может быть критично:
- APK будет больше
- Больше памяти на загрузку
- Дольше инициализация
2. Нельзя использовать в некоторых местах
Inline функции нельзя:
- Передавать как параметр (нельзя получить reference)
- Использовать как interface метод
- Вызывать из другого модуля (если не public inline)
inline fun myFunc() { }
// ✗ Ошибка — нельзя получить reference
val funcRef: () -> Unit = ::myFunc
// ✗ Нельзя переопределить
interface MyInterface {
inline fun doSomething() // Синтаксическая ошибка
}
3. Сложнее отладка
При отладке сложнее прослеживать стек вызовов, т.к. функции нет на runtime:
inline fun getValue(): Int = 42
fun process() {
val value = getValue() // После компиляции это просто: val value = 42
// В stack trace не будет вызова getValue()
}
4. Риск увеличить размер критичного метода
Если inline функция вызывается много раз, код может раздуться:
inline fun complexLogic(): String {
// 50 строк кода
return "result"
}
// Если вызвать 10 раз — 500 строк в бинарнике
for (i in 1..10) {
val result = complexLogic()
}
Когда использовать inline
✓ ИСПОЛЬЗУЙ inline в этих случаях:
1. Функции высокого порядка (высокочастотные)
// Стандартная библиотека делает это
inline fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item) // Lambda подставляется
}
return result
}
// Использование без allocation
val evenNumbers = numbers.filter { it % 2 == 0 }
2. Функции с reified type parameters
// Нужен inline для доступа к Type параметру
inline fun <reified T> parseJson(json: String): T {
val type = T::class
return json.parseAs(type)
}
// Работает только с inline
val user = parseJson<User>(jsonString) // Type информация доступна
3. Очень маленькие функции
// Одна строка — inline имеет смысл
inline fun isEven(x: Int) = x % 2 == 0
// Или простой getter
inline fun getValue() = cachedValue
4. Функции для DSL
// DSL часто использует inline
inline fun html(body: HtmlBuilder.() -> Unit): String {
return HtmlBuilder().apply(body).build()
}
val html = html {
div {
p { text("Hello") }
}
}
✗ НЕ используй inline в этих случаях:
1. Сложные функции
// ✗ Плохо — код раздуется
inline fun processData(): String {
// 100 строк логики
// 10 вложенных условий
// 5 вспомогательных вызовов
return "result"
}
2. Функции, вызываемые из разных мест
// ✗ Плохо — код скопируется везде
inline fun calculateTotal(items: List<Int>): Int {
var sum = 0
for (item in items) sum += item
return sum
}
// Если вызвать из 50 методов — 50 копий кода
3. Рекурсивные функции
// ✗ Ошибка компиляции
inline fun factorial(n: Int): Int {
return if (n <= 1) 1 else n * factorial(n - 1) // Рекурсия не работает
}
4. Функции, возвращающие функции
// ✗ Сложно с inline
inline fun getValidator(): (String) -> Boolean {
return { it.isNotEmpty() }
}
Практические примеры из стандартной библиотеки
Collections API
// Все эти inline для оптимизации
inline fun <T> Iterable<T>.forEach(action: (T) -> Unit) { ... }
inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> { ... }
inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> { ... }
inline fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T? { ... }
Scope functions
// Inline для lambda без allocation
inline fun <T> T.apply(block: T.() -> Unit): T { ... }
inline fun <T, R> T.let(block: (T) -> R): R { ... }
inline fun <T> T.also(block: (T) -> Unit): T { ... }
Правило большого пальца
Используй inline ТОЛЬКО если:
1. Функция маленькая (< 10 строк), ИЛИ
2. Функция передаёт lambda без allocation, ИЛИ
3. Функция использует reified type parameters
ИНАЧЕ — не используй inline
Вывод
- inline улучшает производительность для маленьких функций с lambda параметрами
- inline увеличивает размер бинарника — это overhead для мобильных
- Стандартная библиотека использует inline разумно — используй её как образец
- Компилятор часто инлайнирует автоматически (JVM optimization)
- Не преждевременная оптимизация — используй inline только где есть реальная потребность