Kotlin inline, noinline and crossline

๐Ÿ‘‰  What is an inline function in Kotlin? When we should use it and when not ?


1. Higher-Order Functions with Lambdas

This is the primary reason for inline. If your function accepts a lambda, inlining can prevent the overhead of creating an anonymous class for that lambda.

inline fun<T> measureTime(block: () -> T): T {
 val start = System.currentTimeMillis()
 val result = block()
 println("Elapsed time: ${System.currentTimeMillis() - start}ms")
 return result
}

2. Using reified Type Parameters

In Kotlin/JVM, type information is erased at runtime. If you need to check a generic type (e.g., T is String), you must use an inline function with a reified parameter.

inline fun <reified T> List<Any>.filterIsInstance(): List<T> {
 val result = mutableListOf<T>()
 for (item in this) {
 if (item is T) result.add(item)
 }
 return result
}

3. Non-Local Returns

Inside a lambda passed to an inline function, you can use a naked return to exit the calling function. In a regular (non-inline) lambda, you can only return from the lambda itself using a label.

๐Ÿ‘‰  When to Avoid inline

  • Large Functions: If the function body is large, inlining it everywhere it’s called will significantly increase your binary size (code bloat). Keep inlined functions small.

  • No Lambda Parameters: If your function doesn't take a lambda, there is virtually no performance benefit to inlining it. The compiler generally handles standard function optimization better than manual inlining.

  • Recursive Functions: Inlining a function that calls itself is impossible for the compiler to resolve at compile-time.

๐Ÿ‘‰  What is an noinline andcrosslinein Kotlin? When we should use it?

In Kotlin, when you mark a function as inline, every lambda parameter passed to it is also inlined by default. However, there are times when you need to "opt-out" of this behavior for specific lambdas. That’s where noinline and crossinline come in.

1. noinline

By default, all lambdas in an inline function are inlined. Use noinline when you want a specific lambda to behave like a regular object.

The Problem

Inlined lambdas can only be called inside the function. You cannot store them in a variable, pass them to another non-inline function, or return them.

The Usecase

  • Storage: If you need to save the lambda to a property or a list.

  • Passing around: If you need to pass the lambda as an argument to another function that is not inlined.

Kotlin
 
inline fun doSomething(
    inlinedLambda: () -> Unit, 
    noinline persistentLambda: () -> Unit // This remains an object
) {
    inlinedLambda() // Code is copied here
    
    // We can pass persistentLambda to another function because it's an object
    anotherFunction(persistentLambda) 
}

2. crossinline

crossinline is used when you want the performance benefits of inlining, but you need to prevent the caller from using a non-local return.

The Problem

Standard inline lambdas allow the caller to use return to exit the outer function. However, if the inline function executes the lambda inside another execution context (like a nested lambda, a local object, or a callback), a non-local return would be "illegal" because the return would happen in a different scope than expected.

The Usecase

  • Nested Scopes: When you call the lambda inside another object (like a Runnable or a TimerTask).

  • Callbacks: When the lambda is used inside a higher-order function that is called later.

Kotlin
 
inline fun runDelayed(crossinline block: () -> Unit) {
    val runnable = Runnable {
        block() // 'block' is called inside a nested context
    }
    // Logic to run runnable...
}

// Call site
fun test() {
    runDelayed {
        // return // ERROR: 'return' is not allowed here because of crossinline
    }
}

ModifierCan be stored/passed?Can use "return" (non-local)?Performance
Standard InlineNoYesHighest (Full inlining)
noinlineYesNoNormal (Object creation)
crossinlineNoNoHigh (Inlined, but restricted)

When to use what?

  1. Use inline by default for higher-order functions to save memory.

  2. Add noinline if you get a compiler error saying you can't store the lambda.

  3. Add crossinline if you get a compiler error saying "can't inline because it's called in another context."

Comments

Popular posts from this blog

What is a Coroutine? Why is it better than threads?

What are Coroutine Builders?

Sealed Classes and Sealed Interfaces