funcția inline — mecanism Kotlin prin care corpul funcției este inserat direct în fiecare loc de apel în faza de compilare. Aceasta elimină overhead-ul creat de clasele anonime și obiectele pentru parametrii lambda. Conform Kotlin Documentation, 2025, cuvântul cheie inline este deosebit de eficient pentru funcțiile de ordin superior, unde fiecare lambda fără încorporare creează un obiect FunctionN separat, încărcând garbage collector-ul.
Principalele puncte
Funcția inline este o funcție marcată cu cuvântul cheie inline. Compilatorul Kotlin nu creează pentru ea un bytecode separat cu apel, ci copiază corpul funcției direct în fiecare loc de apel. Scopul principal — optimizarea funcțiilor de ordin superior care acceptă expresii lambda, deoarece fiecare lambda în situația normală creează un obiect anonim al clasei Function.
Conform JetBrains Tech Blog (2024), utilizarea funcțiilor inline în Kotlin poate reduce numărul de obiecte create cu 40–60% în funcțiile care utilizează intens lambde. În bucle și operații cu încărcare mare (sortare, filtrare a colecțiilor) aceasta oferă o creștere măsurabilă a performanței.
Fără inline, fiecare lambda este compilată într-o clasă anonimă (sau o instanță a unui interfață funcțională sintetizată). Pentru lambdele care capturează variabile, se creează obiecte wrapper suplimentare. Funcțiile inline elimină toate aceste obiecte în faza de compilare, înlocuindu-le cu cod direct cu acces la variabile locale fără wrapper.
Utilizați inline doar pentru funcții cu parametri lambda — compilatorul Kotlin avertizează singur dacă inline nu aduce beneficii.
Este suficient să adăugați cuvântul cheie inline înaintea declarației funcției. Compilatorul inserează automat corpul funcției în locurile de apel. Funcția în sine continuă să existe în bytecode pentru cazurile în care nu este apelată direct (de exemplu, din cod Java).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Apel — codul lambda este încorporat în corpul funcției
5.repeatAction { index ->
println("Index: $index")
}
După compilare, codul de mai sus va fi echivalent cu:
// Ce se întâmplă după încorporare (schematic):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
Nu se creează niciun obiect pentru lambda — codul action se execută direct. Aceasta este esența optimizării: în loc de apelul Function.invoke() — inserare directă a codului cu corpul lambdei.
Pentru a verifica încorporarea, deschideți Tools > Kotlin > Show Kotlin Bytecode în IntelliJ IDEA și apăsați Decompile. Veți vedea că în loc de apelul repeatAction cu lambda se generează inserarea directă a corpului funcției cu bucla for.
Fiecare lambda în Kotlin este compilată într-unul din trei variante. Prima — dacă lambda nu capturează variabile, devine o metodă statică a clasei în care este declarată. A doua — dacă capturează o variabilă, se creează o clasă anonimă. A treia — dacă capturează mai multe variabile, se creează o clasă anonimă cu câmpuri pentru fiecare variabilă capturată.
| Tipul lambdei | Fără inline | Cu inline |
|---|---|---|
| Fără captură | O metodă statică (reutilizabilă) | Încorporare completă, fără apel |
| Cu captură a 1 variabilă | Clasă anonimă (un obiect) | Încorporare completă, fără obiect |
| Cu captură a N variabile | Clasă anonimă cu N câmpuri | Încorporare completă, fără obiect |
| Recursivă | Apel normal | inline interzis |
Conform Android Performance Patterns (Google, 2024), în aplicațiile cu utilizare intensivă a colecțiilor (filtrare, sortare, grupare) funcțiile inline reduc numărul de alocări cu 25–35%. Efectul este deosebit de vizibil în Jetpack Compose, unde fiecare schimbare de stare provoacă recompunerea cu multiple lambde.
Lambda într-o funcție obișnuită nu poate executa return din funcția exterioară — doar return local din lambda însăși (prin return@label). În funcția inline, lambda este inserată în corpul funcției apelante, deci nonlocal return devine posibil: return-ul din interiorul lambdei încheie funcția exterioară.
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 în lambda ar returna null din processNumbers()
}
Nonlocal return este convenabil pentru încheierea anticipată, dar poate duce la erori. Dacă lambda este utilizată într-un context nonlocal (salvată într-o variabilă), nonlocal return va provoca o RuntimeException. Compilatorul Kotlin emite un avertisment la încercarea unei astfel de salvări.
Când o funcție are mai mulți parametri lambda, uneori este necesar să se încorporeze doar o parte dintre ei. Pentru aceasta se utilizează noinline — interzice încorporarea unui parametru lambda specific, lăsându-l ca un obiect Function obișnuit.
Modificatorul crossinline rezolvă problema opusă: lambda este încorporată, dar nonlocal return este interzis. Acest lucru este necesar când lambda este utilizată în interiorul altei lambde sau într-un context în care return nu este permis (de exemplu, transmisă în 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 poate fi stocat într-o variabilă sau transmis în altă parte
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
În exemplu onSuccess este marcată ca crossinline — va fi încorporată, dar în interiorul ei nu se poate utiliza return. onError este marcată ca noinline — nu este încorporată, poate fi transmisă ca obiect, salvată în câmpul clasei sau utilizată ca listener.
Funcțiile inline au limitări. Funcțiile recursive inline sunt interzise — compilatorul va emite o eroare. Funcțiile inline nu pot avea vizibilitate private sau internal dacă sunt declarate într-un alt modul, dar această limitare ține de acces, nu de mecanismul de încorporare.
Dimensiunea bytecode-ului crește cu fiecare apel al funcției inline, deoarece corpul este copiat. Conform Kotlin Coding Conventions (JetBrains, 2025), se recomandă utilizarea inline doar pentru funcții de până la 10–15 linii. Pentru funcții mari, beneficiul încorporării lambdelor poate fi anulat de creșterea dimensiunii APK (în Android acest lucru este critic — există limita de 64K metode).
// Practică recomandată
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Nerecomandat pentru funcții mari
inline fun largeComputation(...) { // rău — corp >50 linii
// mai mult de 50 de linii — mai bine de extras într-o funcție obișnuită
}
Funcțiile inline publice în biblioteci necesită prudență: dacă corpul funcției inline se modifică, toți clienții trebuie să recompileze. JetBrains recomandă utilizarea @PublishedApi internal pentru membrii care sunt apelați din funcțiile inline, pentru a păstra compatibilitatea în cadrul modulului.
Întrebări frecvente
Da, funcția de extensie inline funcționează fără limitări. De exemplu: inline fun String.transform(block: (Char) -> Char): String. Extensia nu afectează capacitatea de încorporare — compilatorul o tratează la fel ca pe o funcție inline obișnuită.
Dacă funcția nu acceptă parametri lambda — inline nu oferă avantaj. Compilatorul Kotlin emite un avertisment: "Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types." De asemenea, inline este dăunător pentru funcțiile mari din cauza creșterii bytecode-ului.
inline — modificator de funcție care încorporează corpul acesteia în locul apelului. @JvmInline (value class) — mecanism pentru clase wrapper care sunt înlocuite cu valoarea în timpul compilării. Concepte diferite: inline optimizează apelurile, value class optimizează reprezentarea datelor.
Nu, funcțiile suspend nu pot fi inline, deoarece sunt compilate într-o mașină de stări cu Continuation. Totuși, o funcție inline poate accepta o lambda suspend ca parametru cu crossinline. Aceasta este des utilizată în corutine: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Da, funcțiile inline complică depanarea, deoarece corpul funcției nu este apelat, ci încorporat în locul apelului. Stacktrace-ul devine mai lung, punctele de întrerupere funcționează, dar pot afișa poziții neașteptate. JetBrains recomandă depanarea fără inline, activându-l doar în versiunile release.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și