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.