inline funkce — mechanismus Kotlin, při kterém je tělo funkce vloženo přímo do každého místa volání ve fázi kompilace. Tím se eliminuje režie vytváření anonymních tříd a objektů pro parametry lambda. Podle Kotlin Documentation, 2025 je klíčové slovo inline obzvláště účinné pro funkce vyššího řádu, kde každá lambda bez vkládání vytváří samostatný objekt FunctionN, zatěžující garbage collector.
Hlavní body
Inline funkce je funkce označená klíčovým slovem inline. Kompilátor Kotlin pro ni nevytváří samostatný bajtkód s voláním, ale kopíruje tělo funkce přímo do každého místa volání. Hlavním cílem je optimalizace funkcí vyššího řádu, které přijímají výrazy lambda, protože každá lambda v běžné situaci vytváří anonymní objekt třídy Function.
Podle JetBrains Tech Blog (2024) může použití inline funkcí v Kotlin snížit počet vytvořených objektů o 40–60% ve funkcích, které intenzivně používají lambdy. Ve smyčkách a vysoce zatížených operacích (třídění, filtrování kolekcí) to poskytuje měřitelné zvýšení výkonu.
Bez inline je každá lambda zkompilována do anonymní třídy (nebo instance syntetizovaného funkčního rozhraní). Pro lambdy zachycující proměnné jsou vytvářeny další obalové objekty. Inline funkce eliminují všechny tyto objekty ve fázi kompilace a nahrazují je přímým kódem s přístupem k lokálním proměnným bez obalů.
Používejte inline pouze pro funkce s parametry lambda — kompilátor Kotlin sám varuje, pokud inline nepřináší výhodu.
Stačí přidat klíčové slovo inline před deklaraci funkce. Kompilátor automaticky vloží tělo funkce do míst volání. Samotná funkce nadále existuje v bajtkódu pro případy, kdy není volána přímo (např. z Java kódu).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Volání — kód lambdy je vložen do těla funkce
5.repeatAction { index ->
println("Index: $index")
}
Po kompilaci bude výše uvedený kód ekvivalentní:
// Co se stane po vložení (schematicky):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
Nevytváří se žádný objekt pro lambdu — kód action se provádí přímo. Toto je podstata optimalizace: místo volání Function.invoke() — přímé vložení kódu s tělem lambdy.
Pro kontrolu vkládání otevřete Tools > Kotlin > Show Kotlin Bytecode v IntelliJ IDEA a klikněte na Decompile. Uvidíte, že místo volání repeatAction s lambdou se generuje přímé vložení těla funkce se smyčkou for.
Každá lambda v Kotlin je zkompilována do jedné ze tří variant. První — pokud lambda nezachycuje proměnné, stává se statickou metodou třídy, ve které je deklarována. Druhá — pokud zachycuje jednu proměnnou, je vytvořena anonymní třída. Třetí — pokud zachycuje více proměnných, je vytvořena anonymní třída s poli pro každou zachycenou proměnnou.
| Typ lambdy | Bez inline | S inline |
|---|---|---|
| Bez zachycení | Jedna statická metoda (znovu použitelná) | Plné vložení, bez volání |
| Se zachycením 1 proměnné | Anonymní třída (jeden objekt) | Plné vložení, bez objektu |
| Se zachycením N proměnných | Anonymní třída s N poli | Plné vložení, bez objektu |
| Rekurzivní | Běžné volání | inline zakázáno |
Podle Android Performance Patterns (Google, 2024) v aplikacích s intenzivním používáním kolekcí (filtrování, třídění, seskupování) inline funkce snižují počet alokací o 25–35%. Efekt je zvláště patrný v Jetpack Compose, kde každá změna stavu způsobuje překompozici s mnoha lambdami.
Lambda v běžné funkci nemůže provést return z vnější funkce — pouze lokální return ze samotné lambdy (přes return@label). V inline funkci je lambda vložena do těla volající funkce, proto nonlocal return je možný: return uvnitř lambdy ukončuje vnější funkci.
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 v lambdě by vrátil null z processNumbers()
}
Nonlocal return je vhodný pro předčasné ukončení, ale může vést k chybám. Pokud je lambda použita v nelokálním kontextu (uložena do proměnné), nonlocal return způsobí RuntimeException. Kompilátor Kotlin vydává varování při pokusu o takové uložení.
Když má funkce několik parametrů lambda, někdy je třeba vložit pouze část z nich. K tomu slouží noinline — zakazuje vkládání konkrétního parametru lambda a ponechává jej jako běžný objekt Function.
Modifikátor crossinline řeší opačný problém: lambda je vložena, ale nonlocal return je zakázán. To je potřeba, když je lambda použita uvnitř jiné lambdy nebo v kontextu, kde return není povolen (např. předána do 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 může být uložen v proměnné nebo předán jinam
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
V příkladu je onSuccess označena jako crossinline — bude vložena, ale uvnitř ní nelze použít return. onError je označena jako noinline — není vložena, lze ji předat jako objekt, uložit do pole třídy nebo použít jako listener.
Inline funkce mají omezení. Rekurzivní inline funkce jsou zakázány — kompilátor zobrazí chybu. Inline funkce nemohou mít viditelnost private nebo internal, pokud jsou deklarovány v jiném modulu, ale toto omezení souvisí s přístupem, nikoli se samotným mechanismem vkládání.
Velikost bajtkódu roste s každým voláním inline funkce, protože tělo je kopírováno. Podle Kotlin Coding Conventions (JetBrains, 2025) se doporučuje používat inline pouze pro funkce o velikosti do 10–15 řádků. U velkých funkcí může být výhoda z vkládání lambd anulována zvýšením velikosti APK (v Android je to kritické — existuje limit 64K metod).
// Doporučená praxe
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Nedoporučuje se pro velké funkce
inline fun largeComputation(...) { // špatně — tělo >50 řádků
// více než 50 řádků — raději extrahovat do běžné funkce
}
Veřejné inline funkce v knihovnách vyžadují opatrnost: pokud se tělo inline funkce změní, všichni klienti musí překompilovat. JetBrains doporučuje používat @PublishedApi internal pro členy volané z inline funkcí, aby byla zachována kompatibilita v rámci modulu.
Často kladené otázky
Ano, inline extension funkce funguje bez omezení. Například: inline fun String.transform(block: (Char) -> Char): String. Rozšíření neovlivňuje možnost vkládání — kompilátor ji zpracovává stejně jako běžnou inline funkci.
Pokud funkce nepřijímá parametry lambda — inline neposkytuje výhodu. Kompilátor Kotlin vydává varování: "Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types." Také inline je škodlivý pro velké funkce kvůli růstu bajtkódu.
inline — modifikátor funkce, který vkládá její tělo do místa volání. @JvmInline (value class) — mechanismus pro obalové třídy, které jsou během kompilace nahrazeny hodnotou. Různé koncepty: inline optimalizuje volání, value class optimalizuje reprezentaci dat.
Ne, suspend funkce nemohou být inline, protože jsou kompilovány do stavového stroje s Continuation. Nicméně inline funkce může přijmout suspend lambdu jako parametr s crossinline. To je často používáno v korutinách: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Ano, inline funkce ztěžují ladění, protože tělo funkce není voláno, ale vkládáno do místa volání. Stacktrace se prodlužuje, body přerušení fungují, ale mohou zobrazovat neočekávané pozice. JetBrains doporučuje ladit bez inline a zapínat jej pouze v release sestaveních.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také