inline-functie — een Kotlin-mechanisme waarbij het functielichaam tijdens compilatie direct op elke aanroeplocatie wordt ingevoegd. Dit elimineert de overhead van het maken van anonieme klassen en objecten voor lambdaparameters. Volgens Kotlin Documentation, 2025 is het sleutelwoord inline bijzonder effectief voor hogere-ordefuncties, waar elke lambda zonder inlining een apart FunctionN-object creëert en de garbage collector belast.
Belangrijkste punten
Een inline-functie is een functie gemarkeerd met het sleutelwoord inline. De Kotlin-compiler maakt er geen aparte bytecode met aanroep voor, maar kopieert het functielichaam direct naar elke aanroeplocatie. Het hoofddoel is optimalisatie van hogere-ordefuncties die lambda-expressies accepteren, omdat elke lambda in normale situaties een anoniem object van de klasse Function creëert.
Volgens JetBrains Tech Blog (2024) kan het gebruik van inline-functies in Kotlin het aantal gemaakte objecten met 40–60% verminderen in functies die intensief lambda's gebruiken. In loops en zwaarbelaste operaties (sorteren, filteren van collecties) geeft dit een meetbare prestatieverbetering.
Zonder inline wordt elke lambda gecompileerd naar een anonieme klasse (of een instantie van een gesynthetiseerde functionele interface). Voor lambda's die variabelen vastleggen, worden extra wrapper-objecten gemaakt. Inline-functies elimineren al deze objecten tijdens compilatie en vervangen ze door directe code met toegang tot lokale variabelen zonder wrappers.
Gebruik inline alleen voor functies met lambdaparameters — de Kotlin-compiler waarschuwt zelf als inline geen voordeel biedt.
Voeg eenvoudig het sleutelwoord inline toe vóór de functiedeclaratie. De compiler voegt automatisch het functielichaam in op de aanroepplaatsen. De functie zelf blijft bestaan in bytecode voor gevallen waarin deze niet direct wordt aangeroepen (bijvoorbeeld vanuit Java-code).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Aanroep — lambdacode wordt ingevoegd in het functielichaam
5.repeatAction { index ->
println("Index: $index")
}
Na compilatie is bovenstaande code equivalent aan:
// Wat gebeurt er na inlining (schematisch):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
Er wordt geen object voor de lambda gemaakt — de action-code wordt direct uitgevoerd. Dit is de essentie van optimalisatie: in plaats van een Function.invoke()-aanroep — directe invoeging van code met het lambdalichaam.
Om inlining te controleren, opent u Tools > Kotlin > Show Kotlin Bytecode in IntelliJ IDEA en klikt u op Decompile. U zult zien dat in plaats van de repeatAction-aanroep met lambda, directe invoeging van het functielichaam met een for-lus wordt gegenereerd.
Elke lambda in Kotlin wordt gecompileerd naar een van drie varianten. Eerste — als de lambda geen variabelen vastlegt, wordt het een statische methode van de klasse waarin deze is gedeclareerd. Tweede — als deze één variabele vastlegt, wordt een anonieme klasse gemaakt. Derde — als deze meerdere variabelen vastlegt, wordt een anonieme klasse gemaakt met velden voor elke vastgelegde variabele.
| Type lambda | Zonder inline | Met inline |
|---|---|---|
| Zonder vastlegging | Eén statische methode (hergebruikt) | Volledige inlining, zonder aanroep |
| Met vastlegging van 1 variabele | Anonieme klasse (één object) | Volledige inlining, zonder object |
| Met vastlegging van N variabelen | Anonieme klasse met N velden | Volledige inlining, zonder object |
| Recursief | Normale aanroep | inline verboden |
Volgens Android Performance Patterns (Google, 2024) verminderen inline-functies in apps met intensief gebruik van collecties (filteren, sorteren, groeperen) het aantal allocaties met 25–35%. Het effect is vooral merkbaar in Jetpack Compose, waar elke statusverandering herbewerking met meerdere lambda's veroorzaakt.
Een lambda in een gewone functie kan geen return uitvoeren vanuit de buitenste functie — alleen een lokale return vanuit de lambda zelf (via return@label). In een inline-functie wordt de lambda ingevoegd in het lichaam van de aanroepende functie, dus nonlocal return wordt mogelijk: return binnen de lambda beëindigt de buitenste functie.
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 in lambda zou null retourneren van processNumbers()
}
Nonlocal return is handig voor vroegtijdige beëindiging, maar kan tot fouten leiden. Als de lambda in een niet-lokale context wordt gebruikt (opgeslagen in een variabele), veroorzaakt nonlocal return een RuntimeException. De Kotlin-compiler geeft een waarschuwing bij een poging tot dergelijke opslag.
Wanneer een functie meerdere lambdaparameters heeft, moet soms slechts een deel worden ingevoegd. Hiervoor wordt noinline gebruikt — het verbiedt inlining van een specifieke lambdaparameter, waardoor deze als een gewoon Function-object blijft.
De modifier crossinline lost het tegenovergestelde probleem op: de lambda wordt ingevoegd, maar nonlocal return is verboden. Dit is nodig wanneer de lambda binnen een andere lambda wordt gebruikt of in een context waar return niet is toegestaan (bijvoorbeeld doorgegeven aan 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 kan worden opgeslagen in een variabele of elders worden doorgegeven
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
In het voorbeeld is onSuccess gemarkeerd als crossinline — het wordt ingevoegd, maar er kan geen return binnen worden gebruikt. onError is gemarkeerd als noinline — het wordt niet ingevoegd, kan als object worden doorgegeven, in een klassveld worden opgeslagen of als listener worden gebruikt.
Inline-functies hebben beperkingen. Recursieve inline-functies zijn verboden — de compiler geeft een foutmelding. Inline-functies kunnen geen private of internal zichtbaarheid hebben als ze in een andere module zijn gedeclareerd, maar deze beperking heeft te maken met toegang, niet met het inliningsmechanisme zelf.
De bytecodegrootte neemt toe met elke aanroep van een inline-functie, omdat het lichaam wordt gekopieerd. Volgens Kotlin Coding Conventions (JetBrains, 2025) wordt aanbevolen inline alleen te gebruiken voor functies tot 10–15 regels. Voor grote functies kan het voordeel van lambda-inlining worden tenietgedaan door de toename van de APK-grootte (in Android is dit kritiek — er is een limiet van 64K methoden).
// Aanbevolen praktijk
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Niet aanbevolen voor grote functies
inline fun largeComputation(...) { // slecht — lichaam >50 regels
// meer dan 50 regels — beter om naar een gewone functie te extraheren
}
Publieke inline-functies in bibliotheken vereisen voorzichtigheid: als het lichaam van een inline-functie verandert, moeten alle clients opnieuw compileren. JetBrains raadt het gebruik van @PublishedApi internal aan voor leden die vanuit inline-functies worden aangeroepen, om compatibiliteit binnen de module te behouden.
Veelgestelde vragen
Ja, een inline-extensiefunctie werkt zonder beperkingen. Bijvoorbeeld: inline fun String.transform(block: (Char) -> Char): String. De extensie heeft geen invloed op de inliningsmogelijkheid — de compiler behandelt het als een gewone inline-functie.
Als de functie geen lambdaparameters accepteert — biedt inline geen voordeel. De Kotlin-compiler geeft een waarschuwing: "Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types." Ook is inline schadelijk voor grote functies vanwege de toename van bytecode.
inline — een functiemodifier die het functielichaam invoegt op de aanroepplaats. @JvmInline (value class) — een mechanisme voor wrapperklassen die tijdens compilatie worden vervangen door de waarde. Verschillende concepten: inline optimaliseert aanroepen, value class optimaliseert gegevensrepresentatie.
Nee, suspend-functies kunnen niet inline zijn, omdat ze worden gecompileerd naar een statusmachine met Continuation. Een inline-functie kan echter wel een suspend-lambda als parameter met crossinline accepteren. Dit wordt vaak gebruikt in coroutines: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Ja, inline-functies maken debuggen moeilijker omdat het functielichaam niet wordt aangeroepen maar ingevoegd op de aanroepplaats. De stacktrace wordt langer, breekpunten werken maar kunnen onverwachte posities tonen. JetBrains raadt aan om te debuggen zonder inline en het alleen in release-builds in te schakelen.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook