инлине функција — механизам у Котлину при којем се тело функције убацује директно на свако место позива у фази компилације. Ово елиминише overhead од стварања анонимних класа и објеката за ламбда параметре. Према Kotlin Documentation, 2025, кључна реч inline је посебно ефикасна за функције вишег реда, где свака ламбда без уграђивања ствара засебан објекат FunctionN, оптерећујући сакупљач отпада.
Главно
Инлине функција је функција означена кључном речи inline. Компилатор Котлина не ствара за њу засебан бајткод са позивом, већ копира тело функције директно на свако место позива. Основни циљ — оптимизација функција вишег реда које прихватају ламбда изразе, јер свака ламбда у нормалној ситуацији ствара анонимни објекат класе Function.
Према JetBrains Tech Blog (2024), употреба инлине функција у Котлину може смањити број створених објеката за 40–60% у функцијама које интензивно користе ламбде. У петљама и високо оптерећеним операцијама (сортирање, филтрирање колекција) ово даје мерљив пораст перформанси.
Без inline-а свака ламбда се компилира у анонимну класу (или инстанцу синтетизованог функционалног интерфејса). За ламбде које хватају променљиве стварају се додатни објекти-омотачи. Инлине функције елиминишу све ове објекте у фази компилације, замењујући их директним кодом са приступом локалним променљивим без омотача.
Користите inline само за функције са ламбда параметрима — компилатор Котлина сам упозорава ако inline не доноси корист.
Довољно је додати кључну реч inline пре декларације функције. Компилатор аутоматски убацује тело функције на места позива. Сама функција наставља да постоји у бајткоду за случајеве када се не позива директно (нпр. из Java кода).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Позив — ламбда код се уграђује у тело функције
5.repeatAction { index ->
println("Index: $index")
}
Након компилације, горњи код ће бити еквивалентан:
// Шта се дешава након уграђивања (шематски):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
Не ствара се ниједан објекат за ламбду — код action се извршава директно. Ово је суштина оптимизације: уместо позива Function.invoke() — директно убацивање кода са телом ламбде.
Да бисте проверили уграђивање, отворите Tools > Kotlin > Show Kotlin Bytecode у IntelliJ IDEA и кликните Decompile. Видећете да се уместо позива repeatAction са ламбдом генерише директно убацивање тела функције са for петљом.
Свака ламбда у Котлину се компилира у једну од три варијанте. Прва — ако ламбда не хвата променљиве, постаје статички метод класе у којој је декларисана. Друга — ако хвата једну променљиву, ствара се анонимна класа. Трећа — ако хвата више променљивих, ствара се анонимна класа са пољима за сваку захваћену променљиву.
| Тип ламбде | Без inline | Са inline |
|---|---|---|
| Без хватања | Један статички метод (поново се користи) | Потпуно уграђивање, без позива |
| Са хватањем 1 променљиве | Анонимна класа (један објекат) | Потпуно уграђивање, без објекта |
| Са хватањем N променљивих | Анонимна класа са N поља | Потпуно уграђивање, без објекта |
| Рекурзивна | Обичан позив | inline забрањен |
Према Android Performance Patterns (Google, 2024), у апликацијама са интензивном употребом колекција (филтрирање, сортирање, груписање) инлине функције смањују број алокација за 25–35%. Ефекат је посебно приметан у Jetpack Compose, где свака промена стања изазива поновну композицију са мноштвом ламбди.
Ламбда у обичној функцији не може да изврши return из спољашње функције — само локални return из саме ламбде (преко return@label). У инлине функцији, ламбда се убацује у тело функције која позива, зато nonlocal return постаје могућ: return унутар ламбде завршава спољашњу функцију.
inline fun findFirst(
items: List<Int>,
predicate: (Int) -> Boolean
): Int {
for (item in items) {
if (predicate(item)) {
return item
}
}
return -1
}
fun processNumbers() {
val numbers = listOf(1, 2, 3)
val firstEven = findFirst(numbers) { it % 2 == 0 }
// return у ламбди би вратио null из processNumbers()
}
Nonlocal return је згодан за превремени завршетак, али може довести до грешака. Ако се ламбда користи у нелокалном контексту (чува се у променљивој), nonlocal return ће изазвати RuntimeException. Компилатор Котлина издаје упозорење при покушају таквог чувања.
Када функција има више ламбда параметара, понекад је потребно уградити само део њих. За то се користи noinline — забрањује уграђивање одређеног ламбда параметра, остављајући га као обичан објекат Function.
Модификатор crossinline решава супротан задатак: ламбда се уграђује, али nonlocal return се забрањује. Ово је потребно када се ламбда користи унутар друге ламбде или у контексту где return није дозвољен (нпр. прослеђује се у Runnable).
inline fun processWithCallback(
data: String,
crossinline onSuccess: (String) -> Unit,
noinline onError: (Exception) -> Unit
) {
try {
val result = process(data)
onSuccess(result)
} catch (e: Exception) {
onError(e)
}
}
// noinline: onError може бити ускладиштен у променљивој или прослеђен другде
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
У примеру onSuccess је означена као crossinline — биће уграђена, али унутар ње не може да се користи return. onError је означена као noinline — не уграђује се, може се прослеђивати као објекат, чувати у пољу класе или користити као listener.
Инлине функције имају ограничења. Рекурзивне инлине функције су забрањене — компилатор ће пријавити грешку. Инлине функције не могу имати видљивост private или internal ако су декларисане у другом модулу, али ово ограничење је везано за приступ, а не за сам механизам уграђивања.
Величина бајткода расте са сваким позивом инлине функције, јер се тело копира. Према Kotlin Coding Conventions (JetBrains, 2025), препоручује се коришћење inline само за функције величине до 10–15 линија. За велике функције, добит од уграђивања ламбди може бити поништена повећањем величине APK-а (у Android-у је ово критично — постоји лимит од 64K метода).
// Препоручена пракса
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Није препоручено за велике функције
inline fun largeComputation(...) { // лоше — тело >50 линија
// више од 50 линија — боље издвојити у обичну функцију
}
Јавне инлине функције у библиотекама захтевају опрез: ако се тело инлине функције промени, сви клијенти морају поново да компилирају. JetBrains препоручује коришћење @PublishedApi internal за чланове који се позивају из инлине функција, како би се очувала компатибилност у оквиру модула.
Често постављана питања
Да, inline extension функција ради без ограничења. На пример: inline fun String.transform(block: (Char) -> Char): String. Проширење не утиче на могућност уграђивања — компилатор је обрађује исто као обичну инлине функцију.
Ако функција не прихвата ламбда параметре — inline не даје предност. Компилатор Котлина издаје упозорење: "Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types." Такође, inline је штетан за велике функције због раста бајткода.
inline — модификатор функције који уграђује њено тело на место позива. @JvmInline (value class) — механизам за класе-омотаче које се замењују вредношћу током компилације. Различити концепти: inline оптимизује позиве, value class оптимизује представљање података.
Не, suspend функције не могу бити inline, јер се компилирају у state машину са Continuation. Међутим, инлине функција може прихватити suspend ламбду као параметар са crossinline. Ово се често користи у корутинама: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Да, инлине функције отежавају отклањање грешака, јер се тело функције не позива, већ уграђује на место позива. Stacktrace постаје дужи, тачке прекида раде, али могу приказивати неочекиване позиције. JetBrains препоручује отклањање грешака без inline-а, а укључивање само у release верзијама.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође