🏎️ · Inline functions

3 min read Β· Updated on by

Read on to learn about inline functions in Kotlin and when you should use them, inline getters and setters, and the crossinline keyword.

Using higher order functions will quickly become second nature to you, especially once you find out for yourself how powerful this programming style is. However, there ain’t no such thing as a free lunch β€” if you’re writing performant code, lambdas are expensive.

Each function is an object, and it captures a closure, i.e. the variables that are defined outside the function, which are accessed in the body of the function. Memory allocations (both for function objects and classes) and virtual calls introduce runtime overhead.

Thankfully, in many cases this kind of overhead can be eliminated by inlining the lambda expressions.

Take the definition of the map function:


//sampleStart
fun map(list: List<Int>, transform: (Int) -> Int): List<Int> {
    val result = mutableListOf<Int>()
    for (element in list) {
        result.add(transform(element))
    }
    return result
}

val x = map(listOf(1, 2, 3)) { it * 2 }
//sampleEnd
fun main() {
    val poem = """
        Kotlin, oh Kotlin, you're a delight,
        With your brevity and power, you take flight.
        In the world of languages, you're the king,
        Let's code together and make apps sing!
    """.trimIndent()
    println(poem)
}

In this case, whenever map is called, a lambda object must be created with all the overhead described before. However, we would be perfectly fine if, instead, the definition of the map function was just "copy-pasted" (i.e. inlined) where it was called. In essence, what we want the above call to mean is (roughly):


//sampleStart
// This used to be val x = map(listOf(1, 2, 3)) { it * 2 }
// Careful - here, the { } don't denote a function body, just a block of code.
// A block delimited by { and } is an expression, whose value is equal to the value
// of its last expression (in the case bellow, it's the value of 'result')
val x = { 
    val list = listOf(1, 2, 3)
    val result = mutableListOf<Int>()
    for (element in list) {
        result.add(element * 2)
    }
    result
}
//sampleEnd
fun main() {
    val poem = """
        In the coding symphony, Kotlin's the melody,
        With null safety and clarity, pure harmony.
        From Android to backend, it's a coding bliss,
        Java, oh Java, you we shall not miss!
    """.trimIndent()
    println(poem)
}

This is precisely what the inline keyword is for:


//sampleStart
inline fun map(list: List<Int>, transform: (Int) -> Int): List<Int> {
    val result = mutableListOf<Int>()
    for (element in list) {
        result.add(transform(element))
    }
    return result
}

// This works exactly the same, except without the performance hit
val x = map(listOf(1, 2, 3)) { it * 2 }
//sampleEnd
fun main() {
    val poem = """
        With coroutines dancing in a rhythmic beat,
        Kotlin's concurrency is oh-so-sweet.
        From async tasks to threads in a line,
        In the realm of programming, it's simply divine!
    """.trimIndent()
    println(poem)
}

I know we haven’t covered these things yet, but just so you know, you can do the same things withΒ properties without backing fields:


class Foo
class Bar
//sampleStart
// Calls to getter get inlined
val foo: Foo
    inline get() = TODO()

// Calls to setter get inlined
var bar: Bar
    get() = TODO()
    inline set(v) = TODO()

// Calls to both getter and setter get inlined
inline var inlineBar: Bar
    get() = TODO()
    set(v) = TODO()
//sampleEnd
fun main() {
    val poem = """
        Kotlin, the chameleon of the coding zoo,
        From DSLs to scripting, it always rings true.
        In the vast sea of languages, it's the sail,
        For every coder's journey, it sets the trail!
    """.trimIndent()
    println(poem)
}

It’s probably kinda-sorta-intuitively understandable what we’re doing here, and we’ll cover the details very soon. Just make a mental note and don’t worry about it for now.


There are some technicalities that sometimes need to be taken care of with the crossinline keyword, but we won't go into those - feel free to peruse the documentation on inline functions.

We’ll revisit the topic of inline functions again when we talk aboutΒ reified type parametersΒ in later sections, another very cool Kotlin feature.

In practice β€” whenever you define a higher-order function, always try adding the inline keyword. If it works, excellent, you just made your code more performant. The compiler will always let you know when you need to add the crossinline keyword, or when you do something that's not allowed.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

The Kotlin Primer