ZetCode

Kotlin crossinline Keyword

last modified April 19, 2025

Kotlin's inline functions optimize higher-order functions by inlining lambda expressions. The crossinline modifier restricts non-local returns from lambdas passed to inline functions. This tutorial explores crossinline in depth with practical examples.

Basic Definitions

The crossinline keyword marks lambda parameters in inline functions. It prevents non-local returns from these lambdas while still allowing inlining. This ensures proper control flow in contexts like local objects or nested functions.

Basic crossinline Usage

This example shows the simplest use of crossinline. We define an inline function with a crossinline lambda parameter to prevent non-local returns.

BasicCrossinline.kt
package com.zetcode

inline fun executeCrossinline(crossinline action: () -> Unit) {
    action()
}

fun main() {
    executeCrossinline {
        println("Action executed")
        // return // This would cause a compilation error
    }
}

The executeCrossinline function takes a crossinline lambda. The commented return statement would cause an error because crossinline prevents non-local returns. The function still gets inlined for performance.

crossinline vs Regular Inline

This example contrasts regular inline functions with crossinline variants. It shows how crossinline affects control flow in lambdas.

InlineComparison.kt
package com.zetcode

inline fun regularInline(action: () -> Unit) {
    action()
}

inline fun withCrossinline(crossinline action: () -> Unit) {
    action()
}

fun main() {
    regularInline {
        println("Regular inline")
        return // Allowed: non-local return
    }
    
    withCrossinline {
        println("Crossinline")
        // return // Compilation error: non-local return not allowed
    }
}

The regularInline function allows non-local returns from its lambda. The withCrossinline function prohibits them due to the crossinline modifier. This distinction is crucial for control flow management.

crossinline in Local Objects

crossinline is essential when passing lambdas to local objects or nested functions. This example demonstrates its use in a local object context.

LocalObjectExample.kt
package com.zetcode

inline fun runInObject(crossinline action: () -> Unit) {
    val obj = object {
        fun execute() {
            action()
        }
    }
    obj.execute()
}

fun main() {
    runInObject {
        println("Executing in local object")
        // return // Would cause compilation error
    }
}

Here, the lambda is passed to a local object's method. Without crossinline, a non-local return would be problematic. The crossinline modifier ensures proper control flow within the local object context.

crossinline with Control Structures

This example shows crossinline used with control structures. It demonstrates how crossinline affects returns within loops and conditionals.

ControlStructures.kt
package com.zetcode

inline fun processItems(
    items: List<Int>,
    crossinline processor: (Int) -> Unit
) {
    for (item in items) {
        if (item < 0) {
            processor(item)
            // return // Not allowed here with crossinline
        }
    }
}

fun main() {
    val numbers = listOf(1, -2, 3, -4)
    processItems(numbers) {
        println("Processing negative: $it")
    }
}

The processItems function uses crossinline for its processor lambda. While local returns are allowed, non-local returns are prohibited. This maintains predictable control flow in the loop structure.

crossinline in Library Functions

Many Kotlin standard library functions use crossinline. This example mimics such usage to show practical applications.

LibraryStyle.kt
package com.zetcode

inline fun <T> T.applyCrossinline(
    crossinline block: T.() -> Unit
): T {
    val receiver = this
    receiver.block()
    return receiver
}

fun main() {
    val message = StringBuilder().applyCrossinline {
        append("Hello")
        append(", ")
        append("Kotlin")
        // return // Would cause compilation error
    }
    println(message.toString())
}

This applyCrossinline function mimics Kotlin's standard apply function but with crossinline. It ensures the lambda doesn't contain non-local returns while still being inlined for performance.

crossinline with Multiple Lambdas

Functions can have both regular and crossinline lambda parameters. This example shows how they interact in the same function.

MultipleLambdas.kt
package com.zetcode

inline fun handleEvents(
    regularAction: () -> Unit,
    crossinline safeAction: () -> Unit
) {
    regularAction()
    
    val handler = object {
        fun handle() {
            safeAction()
        }
    }
    handler.handle()
}

fun main() {
    handleEvents(
        { println("Regular action"); return },
        { println("Safe action") /* return not allowed */ }
    )
}

The function has one regular inline lambda and one crossinline lambda. The regular lambda allows non-local returns, while the crossinline one doesn't. This combination provides flexibility in different usage scenarios.

crossinline in Real-World Scenarios

This example demonstrates a practical use case for crossinline in a resource management scenario, similar to Kotlin's use in the standard library.

ResourceManagement.kt
package com.zetcode

inline fun <T : AutoCloseable, R> T.useCrossinline(
    crossinline block: (T) -> R
): R {
    try {
        return block(this)
    } finally {
        close()
    }
}

fun main() {
    val result = System.`in`.bufferedReader().useCrossinline {
        it.readLine()
        // return // Would cause compilation error
    }
    println("Read: $result")
}

This useCrossinline function mimics Kotlin's use function but with crossinline. It ensures proper resource cleanup by preventing non-local returns that could skip the finally block. The lambda still gets inlined for performance.

Best Practices for crossinline

Source

Kotlin Inline Functions Documentation

This tutorial covered Kotlin's crossinline keyword in depth, showing its role in inline functions. We explored various scenarios including local objects, control structures, and resource management. Proper use of crossinline helps maintain predictable control flow while preserving inlining benefits.

Author

My name is Jan Bodnar, and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all Kotlin tutorials.