Inline-Funktion — ein Kotlin-Mechanismus, bei dem der Funktionskörper zur Kompilierzeit direkt an jeder Aufrufstelle eingesetzt wird. Dies beseitigt den Overhead durch die Erstellung anonymer Klassen und Objekte für Lambda-Parameter. Laut Kotlin Documentation, 2025 ist das Schlüsselwort inline besonders effektiv für Higher-Order-Funktionen, wo jedes Lambda ohne Inlining ein separates FunctionN-Objekt erzeugt und den Garbage Collector belastet.
Wichtige Punkte
Eine Inline-Funktion ist eine Funktion, die mit dem Schlüsselwort inline markiert ist. Der Kotlin-Compiler erstellt keinen separaten Bytecode mit einem Aufruf dafür — stattdessen kopiert er den Funktionskörper direkt an jede Aufrufstelle. Das Hauptziel ist die Optimierung von Higher-Order-Funktionen, die Lambda-Ausdrücke akzeptieren, da jedes Lambda in einer normalen Situation ein anonymes Function-Klassenobjekt erstellt.
Laut JetBrains Tech Blog (2024) kann die Verwendung von Inline-Funktionen in Kotlin die Anzahl der erstellten Objekte um 40–60% in Funktionen reduzieren, die Lambdas intensiv nutzen. In Schleifen und Hochlastoperationen (Sortieren, Filtern von Sammlungen) bietet dies einen messbaren Leistungsgewinn.
Ohne inline wird jedes Lambda in eine anonyme Klasse (oder eine Instanz einer synthetisierten funktionalen Schnittstelle) kompiliert. Für Lambdas, die Variablen erfassen, werden zusätzliche Wrapper-Objekte erstellt. Inline-Funktionen eliminieren all diese Objekte zur Kompilierzeit und ersetzen sie durch direkten Code, der ohne Wrapper auf lokale Variablen zugreift.
Verwenden Sie inline nur für Funktionen mit Lambda-Parametern — der Kotlin-Compiler warnt selbst, wenn inline keinen Vorteil bringt.
Fügen Sie einfach das Schlüsselwort inline vor der Funktionsdeklaration hinzu. Der Compiler setzt automatisch den Funktionskörper an den Aufrufstellen ein. Die Funktion selbst existiert im Bytecode weiterhin für Fälle, in denen sie nicht direkt aufgerufen wird (z. B. aus Java-Code).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Aufruf — Lambda-Code wird in den Funktionskörper eingefügt
5.repeatAction { index ->
println("Index: $index")
}
Nach der Kompilierung ist der obige Code äquivalent zu:
// Was nach dem Inlining passiert (schematisch):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
Es werden keine Objekte für das Lambda erstellt — der Action-Code wird direkt ausgeführt. Dies ist der Kern der Optimierung: statt Function.invoke() aufzurufen — eine direkte Einfügung des Codes mit dem Lambda-Körper.
Um das Inlining zu überprüfen, öffnen Sie Tools > Kotlin > Show Kotlin Bytecode in IntelliJ IDEA und klicken Sie auf Decompile. Sie werden sehen, dass statt des Aufrufs von repeatAction mit einem Lambda eine direkte Einfügung des Funktionskörpers mit einer for-Schleife generiert wird.
Jedes Lambda in Kotlin wird in eine von drei Varianten kompiliert. Erste — wenn das Lambda keine Variablen erfasst, wird es eine statische Methode der Klasse, in der es deklariert ist. Zweite — wenn es eine Variable erfasst, wird eine anonyme Klasse erstellt. Dritte — wenn es mehrere Variablen erfasst, wird eine anonyme Klasse mit Feldern für jede erfasste Variable erstellt.
| Lambda-Typ | Ohne inline | Mit inline |
|---|---|---|
| Ohne Erfassung | Eine statische Methode (wiederverwendet) | Vollständiges Inlining, kein Aufruf |
| Mit Erfassung von 1 Variable | Anonyme Klasse (ein Objekt) | Vollständiges Inlining, kein Objekt |
| Mit Erfassung von N Variablen | Anonyme Klasse mit N Feldern | Vollständiges Inlining, kein Objekt |
| Rekursiv | Normaler Aufruf | inline verboten |
Laut Android Performance Patterns (Google, 2024) reduzieren Inline-Funktionen in Anwendungen mit intensiver Sammlungsnutzung (Filtern, Sortieren, Gruppieren) die Allokationen um 25–35%. Der Effekt ist besonders in Jetpack Compose bemerkbar, wo jede Zustandsänderung eine Neuzusammensetzung mit vielen Lambdas auslöst.
Ein Lambda in einer normalen Funktion kann nicht von der äußeren Funktion zurückkehren — nur ein lokaler return vom Lambda selbst (über return@label). In einer Inline-Funktion wird das Lambda in den Körper der aufrufenden Funktion eingefügt, daher wird nonlocal return möglich: ein return innerhalb des Lambdas beendet die äußere Funktion.
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 im Lambda würde null von processNumbers() zurückgeben
}
Nonlocal return ist praktisch für die vorzeitige Beendigung, kann aber zu Fehlern führen. Wenn das Lambda in einem nicht-lokalen Kontext verwendet wird (in einer Variablen gespeichert), führt ein nonlocal return zu einer RuntimeException. Der Kotlin-Compiler gibt eine Warnung aus, wenn ein solches Speichern versucht wird.
Wenn eine Funktion mehrere Lambda-Parameter hat, müssen manchmal nur einige davon inline eingefügt werden. Dafür wird noinline verwendet — es verhindert das Inlining eines bestimmten Lambda-Parameters und belässt ihn als normales Function-Objekt.
Der Modifikator crossinline löst das gegenteilige Problem: das Lambda wird inline eingefügt, aber nonlocal return ist verboten. Dies ist erforderlich, wenn das Lambda innerhalb eines anderen Lambdas oder in einem Kontext verwendet wird, in dem return nicht erlaubt ist (z. B. an einen Runnable übergeben).
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 kann in einer Variable gespeichert oder anderswo übergeben werden
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
Im Beispiel ist onSuccess als crossinline markiert — es wird inline eingefügt, aber return kann darin nicht verwendet werden. onError ist als noinline markiert — es wird nicht inline eingefügt, kann also als Objekt übergeben, in einem Klassenfeld gespeichert oder als Listener verwendet werden.
Inline-Funktionen haben Einschränkungen. Rekursive Inline-Funktionen sind verboten — der Compiler gibt einen Fehler zurück. Inline-Funktionen können keine private- oder internal-Sichtbarkeit haben, wenn sie in einem anderen Modul deklariert sind, aber dies ist eine Sichtbarkeitseinschränkung, nicht mit dem Inlining-Mechanismus selbst verbunden.
Die Bytecode-Größe wächst mit jedem Inline-Funktionsaufruf, da der Körper kopiert wird. Laut Kotlin Coding Conventions (JetBrains, 2025) wird empfohlen, inline nur für Funktionen bis zu 10–15 Zeilen zu verwenden. Bei großen Funktionen kann der Vorteil des Lambda-Inlinings durch die APK-Größensteigerung zunichte gemacht werden (kritisch in Android aufgrund des 64K-Methodenlimits).
// Empfohlene Vorgehensweise
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Nicht für große Funktionen empfohlen
inline fun largeComputation(...) { // schlecht — Körper >50 Zeilen
// mehr als 50 Zeilen — besser in eine reguläre Funktion auslagern
}
Öffentliche Inline-Funktionen in Bibliotheken erfordern Vorsicht: Wenn sich der Körper einer Inline-Funktion ändert, müssen alle Clients neu kompiliert werden. JetBrains empfiehlt die Verwendung von @PublishedApi internal für Member, die von Inline-Funktionen aufgerufen werden, um die Kompatibilität innerhalb eines Moduls zu wahren.
Häufig gestellte Fragen
Ja, eine Inline-Erweiterungsfunktion funktioniert ohne Einschränkungen. Beispiel: inline fun String.transform(block: (Char) -> Char): String. Die Erweiterung beeinträchtigt nicht die Inlining-Fähigkeit — der Compiler behandelt sie wie eine normale Inline-Funktion.
Wenn die Funktion keine Lambda-Parameter akzeptiert — bringt inline keinen Vorteil. Der Kotlin-Compiler gibt eine Warnung aus: „Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types.“ Außerdem ist inline aufgrund des Bytecode-Wachstums für große Funktionen schädlich.
inline — ein Funktionsmodifikator, der den Funktionskörper an der Aufrufstelle inline einfügt. @JvmInline (value class) — ein Mechanismus für Wrapper-Klassen, die zur Kompilierzeit durch ihren Wert ersetzt werden. Unterschiedliche Konzepte: inline optimiert Aufrufe, value class optimiert die Datendarstellung.
Nein, suspend-Funktionen können nicht inline sein, da sie in eine Zustandsmaschine mit Continuation kompiliert werden. Eine Inline-Funktion kann jedoch mit crossinline einen suspend-Lambda als Parameter akzeptieren. Dies wird häufig in Koroutinen verwendet: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Ja, Inline-Funktionen erschweren das Debugging, da der Funktionskörper nicht aufgerufen, sondern an der Aufrufstelle inline eingefügt wird. Stacktraces werden länger, Breakpoints funktionieren, können aber unerwartete Positionen anzeigen. JetBrains empfiehlt, ohne inline zu debuggen und es nur in Release-Builds zu aktivieren.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch