Fundamentals 12 min read

Kotlin Inline Functions: More Than Just a Performance Trick

This article explains how Kotlin's inline keyword eliminates lambda object allocation and virtual calls, enables non‑local returns and reified generics, discusses inline properties and their performance benefits, and outlines scenarios where inlining can backfire, helping developers use it wisely.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Kotlin Inline Functions: More Than Just a Performance Trick

What inline does

Kotlin marks a function with inline to replace each call site with the function body, removing the need to create a Function object for a lambda and avoiding virtual invoke() calls. The compiler therefore reduces allocation, memory overhead, and call‑time indirection.

inline fun performOperation(operation: () -> Unit) {
    println("Starting operation...")
    operation()
    println("Operation completed.")
}

fun main() {
    performOperation { println("关注 RockByte 公众号") }
}

When compiled, the call to performOperation is expanded to the body above, embedding the lambda directly and eliminating the Function object.

Performance benefits

Without inline, each lambda argument creates a Function object, consumes heap memory, and incurs a virtual method call. In tight loops these costs accumulate, increasing GC pressure. Inlining removes object creation and virtual dispatch, delivering the core performance gain.

Non‑local returns

In a regular lambda you can only exit the lambda with return@label. An inline lambda can use a bare return, which exits the surrounding function because the lambda code is physically placed inside it.

inline fun forEach(numbers: List<Int>, action: (Int) -> Unit) {
    for (number in numbers) {
        action(number)
    }
}

fun printFirstNegative(numbers: List<Int>) {
    forEach(numbers) { if (it < 0) {
        println("找到第一个负数:$it")
        return // exits printFirstNegative
    } }
    println("遍历完成,没有找到负数")
}

If forEach were not inline, the bare return would be illegal and you would need return@forEach.

Reified type parameters

On the JVM generic types are erased, so you cannot normally write T::class. Declaring an inline function with reified preserves the actual type at runtime, enabling checks such as value is T.

inline fun <reified T> isInstance(value: Any): Boolean {
    return value is T
}

fun main() {
    println(isInstance<String>("Hello")) // true
    println(isInstance<Int>("Hello"))    // false
}

When not to inline

Code‑size bloat : Large functions copied to many call sites increase binary size and can cause cache misses.

Large functions : Functions with extensive logic should stay non‑inline to avoid duplicated bytecode.

Overriding : Inline functions are implicitly final, preventing polymorphic overrides.

No lambda parameter : Without a lambda the main advantage disappears; the JIT often inlines such functions better.

Recursive or private functions : Recursion is not supported, and private functions without lambdas gain little.

Inline properties (advanced)

When a property accessor is marked inline, its getter or setter code is inserted at the call site, removing the method‑call overhead. This is useful for simple, frequently accessed properties.

inline var calculatedValue: Int
    get() = someComplexCalculation()
    set(value) { saveResult(value) }

Accessing calculatedValue now executes the getter or setter logic directly without a method call.

Inline functions with suspend lambdas

Standard library functions like repeat(), map(), and filter() are inline. Although they are not suspend, they can accept a suspend lambda because the lambda body is inlined into the surrounding coroutine.

suspend fun printMessage(message: String) {
    println("Message: $message")
}

suspend fun main() {
    repeat(3) { printMessage("skydoves $it") }
}

The compiler expands repeat(3) into a simple for loop, allowing the suspend lambda to run inside the coroutine context.

Summary

The inline keyword reduces allocation and virtual‑call overhead for higher‑order functions, enables non‑local returns, and, together with reified, retains generic type information at runtime. Inline properties bring the same benefits to simple accessors. Use inline judiciously: avoid large functions, recursive cases, and situations where code size would explode.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performance optimizationAndroidKotlininline functionsreified genericsnon-local return
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.