inline function — a Kotlin mechanism where the function body is substituted directly at each call site at compile time. This eliminates the overhead of creating anonymous classes and objects for lambda parameters. According to Kotlin Documentation, 2025, the inline keyword is especially effective for higher-order functions, where each lambda without inlining creates a separate FunctionN object, burdening the garbage collector.
Key Takeaways
An inline function is a function marked with the inline keyword. The Kotlin compiler does not create separate bytecode with a call for it — instead, it copies the function body directly into each call site. The main goal is to optimize higher-order functions that accept lambda expressions, since each lambda in a normal situation creates an anonymous Function class object.
According to JetBrains Tech Blog (2024), using inline functions in Kotlin can reduce the number of created objects by 40–60% in functions that intensively use lambdas. In loops and high-load operations (sorting, collection filtering) this provides a measurable performance boost.
Without inline, each lambda compiles into an anonymous class (or an instance of a synthesized functional interface). For lambdas that capture variables, additional wrapper objects are created. Inline functions eliminate all these objects at compile time, replacing them with direct code that accesses local variables without wrappers.
Use inline only for functions with lambda parameters — the Kotlin compiler itself warns if inline does not provide any benefit.
Simply add the inline keyword before the function declaration. The compiler automatically substitutes the function body at the call sites. The function itself continues to exist in bytecode for cases when it is not called directly (for example, from Java code).
inline fun Int.repeatAction(action: (Int) -> Unit) {
for (i in 0 until this) {
action(i)
}
}
// Call — lambda code is inlined into the function body
5.repeatAction { index ->
println("Index: $index")
}
After compilation, the code above will be equivalent to:
// What happens after inlining (schematically):
val $this = 5
for (i in 0 until $this) {
println("Index: $i")
}
No objects are created for the lambda — the action code executes directly. This is the essence of the optimization: instead of calling Function.invoke() — a direct insertion of the code with the lambda body.
To verify inlining, open Tools > Kotlin > Show Kotlin Bytecode in IntelliJ IDEA and click Decompile. You will see that instead of calling repeatAction with a lambda, a direct insertion of the function body with a for loop is generated.
Each lambda in Kotlin compiles into one of three variants. First — if the lambda does not capture variables, it becomes a static method of the class where it is declared. Second — if it captures one variable, an anonymous class is created. Third — if it captures multiple variables, an anonymous class with fields for each captured variable is created.
| Lambda type | Without inline | With inline |
|---|---|---|
| No capture | One static method (reused) | Full inlining, no call |
| 1 variable capture | Anonymous class (one object) | Full inlining, no object |
| N variables capture | Anonymous class with N fields | Full inlining, no object |
| Recursive | Normal call | inline is prohibited |
According to Android Performance Patterns (Google, 2024), in applications with intensive collection usage (filtering, sorting, grouping) inline functions reduce allocations by 25–35%. The effect is especially noticeable in Jetpack Compose, where each state change triggers recomposition with many lambdas.
A lambda in a regular function cannot return from the outer function — only a local return from the lambda itself (via return@label). In an inline function, the lambda is inlined into the calling function's body, so nonlocal return becomes possible: a return inside the lambda terminates the outer function.
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 would return null from processNumbers()
}
Nonlocal return is convenient for early termination but can lead to errors. If the lambda is used in a non-local context (stored in a variable), a nonlocal return will cause a RuntimeException. The Kotlin compiler issues a warning when attempting such storage.
When a function has multiple lambda parameters, sometimes you need to inline only some of them. noinline is used for this — it prevents inlining of a specific lambda parameter, leaving it as a regular Function object.
The crossinline modifier solves the opposite problem: the lambda is inlined, but nonlocal return is prohibited. This is needed when the lambda is used inside another lambda or in a context where return is not allowed (for example, passed to a 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 can be stored in a variable or passed elsewhere
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)
In the example, onSuccess is marked as crossinline — it will be inlined, but return cannot be used inside it. onError is marked as noinline — it is not inlined, so it can be passed as an object, stored in a class field, or used as a listener.
Inline functions have limitations. Recursive inline functions are prohibited — the compiler will return an error. Inline functions cannot have private or internal visibility if declared in another module, but this is a visibility limitation, not related to the inlining mechanism itself.
The bytecode size grows with each inline function call, since the body is copied. According to Kotlin Coding Conventions (JetBrains, 2025), it is recommended to use inline only for functions up to 10–15 lines. For large functions, the benefit from inlining lambdas may be negated by the APK size increase (critical in Android due to the 64K method limit).
// Recommended practice
inline fun withLock(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
// Not recommended for large functions
inline fun largeComputation(...) { // bad — body >50 lines
// more than 50 lines — better to extract into a regular function
}
Public inline functions in libraries require caution: if the body of an inline function changes, all clients must be recompiled. JetBrains recommends using @PublishedApi internal for members called from inline functions to maintain compatibility within a module.
Frequently Asked Questions
Yes, inline extension function works without restrictions. For example: inline fun String.transform(block: (Char) -> Char): String. Extension does not affect the ability to inline — the compiler handles it the same way as a regular inline function.
If the function does not accept lambda parameters — inline provides no advantage. The Kotlin compiler issues a warning: “Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types.” Also, inline is harmful for large functions due to bytecode growth.
inline — a function modifier that inlines the function body at the call site. @JvmInline (value class) — a mechanism for wrapper classes that are replaced by their value at compile time. Different concepts: inline optimizes calls, value class optimizes data representation.
No, suspend functions cannot be inline because they compile into a state machine with Continuation. However, an inline function can accept a suspend lambda as a parameter with crossinline. This is often used in coroutines: inline fun launch(block: suspend CoroutineScope.() -> Unit).
Yes, inline functions complicate debugging because the function body is not called but inlined at the call site. Stacktraces become longer, breakpoints work but may show unexpected positions. JetBrains recommends debugging without inline and enabling it only in release builds.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also