inline függvény — Kotlin mechanizmus, amelynél a függvény törzse közvetlenül a hívás helyére illesztődik be a fordítási fázisban. Ez kiküszöböli a névtelen osztályok és objektumok lambda paraméterekhez történő létrehozásának overhead-jét. A Kotlin Documentation, 2025 szerint az inline kulcsszó különösen hatékony magasabb rendű függvényeknél, ahol minden lambda beillesztés nélkül külön FunctionN objektumot hoz létre, terhelve a garbage collector-t.
Főbb pontok
Az inline függvény egy inline kulcsszóval megjelölt függvény. A Kotlin fordító nem hoz létre számára külön bájtkódot hívással, hanem közvetlenül a hívás minden helyére másolja a függvény törzsét. A fő cél — a magasabb rendű függvények optimalizálása, amelyek lambda kifejezéseket fogadnak el, mivel minden lambda normál helyzetben létrehozza a Function osztály egy névtelen objektumát.
A JetBrains Tech Blog (2024) szerint az inline függvények használata Kotlinban 40–60%-kal csökkentheti a létrehozott objektumok számát a lambdákat intenzíven használó függvényekben. Ciklusokban és nagy terhelésű műveletekben (rendezés, gyűjtemények szűrése) ez mérhető teljesítménynövekedést eredményez.
Inline nélkül minden lambda egy névtelen osztályba (vagy egy szintetizált funkcionális interfész példányába) fordul. A változókat lezáró lambdák számára további wrapper objektumok jönnek létre. Az inline függvények ezeket az objektumokat a fordítási fázisban megszüntetik, helyükre közvetlen kódot téve, hozzáféréssel a helyi változókhoz wrapper nélkül.
Csak lambda paraméterekkel rendelkező függvényekhez használja az inline-t — a Kotlin fordító maga figyelmeztet, ha az inline nem nyújt előnyt.
Elég az inline kulcsszót hozzáadni a függvény deklarációja elé. A fordító automatikusan beilleszti a függvény törzsét a hívási helyekre. Maga a függvény továbbra is létezik a bájtkódban azokra az esetekre, amikor nem közvetlenül hívják (pl. Java kódból).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Hívás — a lambda kód beillesztődik a függvény törzsébe
5.repeatAction { index ->
println("Index: $index")
}
A fordítás után a fenti kód egyenértékű lesz a következővel:
// Mi történik a beillesztés után (vázlatosan):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
Nem jön létre objektum a lambda számára — az action kód közvetlenül végrehajtódik. Ez az optimalizálás lényege: a Function.invoke() hívása helyett — a kód közvetlen beillesztése a lambda törzsével.
A beillesztés ellenőrzéséhez nyissa meg a Tools > Kotlin > Show Kotlin Bytecode menüpontot az IntelliJ IDEA-ban, és kattintson a Decompile gombra. Látni fogja, hogy a lambda-val történő repeatAction hívás helyett a függvény törzsének közvetlen beillesztése for ciklussal generálódik.
Minden lambda Kotlinban a három változat egyikébe fordul. Első — ha a lambda nem zár le változókat, annak az osztálynak a statikus metódusává válik, amelyben deklarálták. Második — ha egy változót zár le, névtelen osztály jön létre. Harmadik — ha több változót zár le, névtelen osztály jön létre mezőkkel minden lezárt változóhoz.
| Lambda típusa | Inline nélkül | Inline-nal |
|---|---|---|
| Lezárás nélkül | Egy statikus metódus (újrahasznosítható) | Teljes beillesztés, hívás nélkül |
| 1 változó lezárásával | Névtelen osztály (egy objektum) | Teljes beillesztés, objektum nélkül |
| N változó lezárásával | Névtelen osztály N mezővel | Teljes beillesztés, objektum nélkül |
| Rekurzív | Szokásos hívás | inline tiltott |
Az Android Performance Patterns (Google, 2024) szerint a gyűjteményeket intenzíven használó alkalmazásokban (szűrés, rendezés, csoportosítás) az inline függvények 25–35%-kal csökkentik a foglalások számát. A hatás különösen észrevehető a Jetpack Compose-ban, ahol minden állapotváltozás újrakompozíciót okoz számos lambdával.
A lambda egy szokásos függvényben nem hajthat végre return-t a külső függvényből — csak lokális return-t magából a lambdából (return@label-en keresztül). Az inline függvényben a lambda a hívó függvény törzsébe illesztődik, ezért nonlocal return válik lehetővé: a lambda belsejében lévő return befejezi a külső függvényt.
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 a lambdában null-t adna vissza a processNumbers()-ből
}
Nonlocal return kényelmes a korai befejezéshez, de hibákhoz vezethet. Ha a lambdát nem lokális kontextusban használják (változóba mentik), a nonlocal return RuntimeException-t okoz. A Kotlin fordító figyelmeztetést ad az ilyen mentési kísérletnél.
Amikor egy függvénynek több lambda paramétere van, néha csak egy részüket kell beilleszteni. Erre szolgál a noinline — megtiltja egy adott lambda paraméter beillesztését, meghagyva azt szokásos Function objektumként.
A crossinline módosító az ellenkező problémát oldja meg: a lambda beillesztődik, de a nonlocal return tiltott. Ez akkor szükséges, amikor a lambdát egy másik lambdán belül vagy olyan kontextusban használják, ahol a return nem engedélyezett (pl. Runnable-be továbbítva).
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: az onError tárolható változóban vagy továbbítható máshová
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
A példában az onSuccess crossinline-ként van megjelölve — beillesztésre kerül, de belül nem használható return. Az onError noinline-ként van megjelölve — nem illesztődik be, objektumként továbbítható, osztály mezőben tárolható vagy listenerként használható.
Az inline függvényeknek korlátozásaik vannak. Rekurzív inline függvények tiltottak — a fordító hibát jelez. Az inline függvények nem rendelkezhetnek private vagy internal láthatósággal, ha másik modulban deklarálták őket, de ez a korlátozás a hozzáférésre vonatkozik, nem magára a beillesztési mechanizmusra.
A bájtkód mérete minden inline függvényhívással nő, mivel a törzs másolódik. A Kotlin Coding Conventions (JetBrains, 2025) szerint ajánlott az inline-t csak 10–15 soros függvényekhez használni. Nagy függvényeknél a lambdák beillesztéséből származó előnyt semlegesítheti az APK méretének növekedése (Androidban ez kritikus — létezik 64K metódus korlát).
// Ajánlott gyakorlat
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Nem ajánlott nagy függvényekhez
inline fun largeComputation(...) { // rossz — törzs >50 sor
// több mint 50 sor — jobb kivonni egy szokásos függvénybe
}
A könyvtárakban lévő publikus inline függvények óvatosságot igényelnek: ha az inline függvény törzse megváltozik, minden kliensnek újra kell fordítania. A JetBrains a @PublishedApi internal használatát ajánlja az inline függvényekből hívott tagok számára a modulon belüli kompatibilitás megőrzése érdekében.
Gyakran Ismételt Kérdések
Igen, az inline kiterjesztő függvény korlátozások nélkül működik. Például: inline fun String.transform(block: (Char) -> Char): String. A kiterjesztés nem befolyásolja a beilleszthetőséget — a fordító ugyanúgy kezeli, mint egy szokásos inline függvényt.
Ha a függvény nem fogad el lambda paramétereket — az inline nem nyújt előnyt. A Kotlin fordító figyelmeztetést ad: "Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types." Továbbá az inline káros a nagy függvényekre a bájtkód növekedése miatt.
inline — függvény módosító, amely a függvény törzsét a hívás helyére illeszti. @JvmInline (value class) — mechanizmus a wrapper osztályok számára, amelyek a fordítás során értékkel helyettesítődnek. Különböző koncepciók: inline a hívásokat optimalizálja, value class az adatábrázolást optimalizálja.
Nem, a suspend függvények nem lehetnek inline, mivel állapotgéppé fordulnak Continuation-nel. Az inline függvény azonban fogadhat suspend lambdát paraméterként crossinline-nal. Ezt gyakran használják korutinokban: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Igen, az inline függvények megnehezítik a hibakeresést, mivel a függvény törzse nem hívódik, hanem a hívás helyére illesztődik be. A stacktrace hosszabb lesz, a töréspontok működnek, de váratlan pozíciókat mutathatnak. A JetBrains azt ajánlja, hogy inline nélkül végezze a hibakeresést, és csak release build-ekben kapcsolja be.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is