7 Kotlin Keywords You Never Used

Written by philipplackner

10. October 2020

Kotlin provides many cool and useful keywords. Not all of them are used often though, but you can still improve your code’s style or performance a lot by using them. My Kotlin Course covers the basics of Kotlin, but the keywords I mention here are more advanced and require a little explanation.

1. Inline

The inline-keyword is probably one you have seen in the past, but many people don’t know what it really does because you don’t see any immediate effect by using it. But, it can have a big effect on your program’s performance.

So, what does it do? In most programming languages, functions are treated as objects and Kotlin is one of these languages. Normally, every function has its own address in memory, so the program knows where in memory it can find the function code when this particular function is called. Also, functions have to allocate memory for objects and variables they use.

Inline will erase this behavior! Let’s look at this simple custom filter function:

fun List<Int>.myFilter(predicate: (Int) -> Boolean): List<Int> {
    val result = mutableListOf<Int>()
    for(number in this) {
        if(predicate(number)) result.add(number)
    }
    return result
}

fun main() {
    var list = listOf(1, 2, 3, 4, 5, 6, 7, 8)
    list = list.myFilter { it % 2 == 0 }
    println(list)
}

That’s a normal function in Kotlin. When it is called, the CPU will look at the function’s address to find its source code and execute it. The same counts for the lambda function passed to it as a parameter. Especially, lambda functions are usually very short. Let’s see what would happen, if we would make myFilter an inline function:

inline fun List<Int>.myFilter(predicate: (Int) -> Boolean): List<Int> {
    val result = mutableListOf<Int>()
    for(number in this) {
        if(predicate(number)) result.add(number)
    }
    return result
}

fun main() {
    var list = listOf(1, 2, 3, 4, 5, 6, 7, 8)
    
    // this would be replaced
    list = list.myFilter { it % 2 == 0 }
    
    // with this
    val result = mutableListOf<Int>()
    for(number in list) {
        if(number % 2 == 0) result.add(number)
    }
    
    println(list)
}

As you can see, with inline, the compiler takes the actual code of the lambda function and copies it to the place where it is called. So, this lambda function doesn’t have its own address, instead its code is simply copied over. So, if you use inline for these smaller functions, you can actually make your code more efficient. Just don’t use it for large functions as it will copy over the code of them for each function call.

2. Noinline

If you have a function that takes multiple lambdas and you only want to inline a few of them, you can use the noinline keyword for the other ones:

inline fun doSomething(inlined: () -> Unit, noinline notInlined: () -> Unit) {
    // ...
}

3. Crossinline

Wait, another inline keyword?! Yes! Take a look at this code:

inline fun doSomething(crossinline lambda: () -> Unit) {
    GlobalScope.launch { 
        lambda()
    }
}

fun main() {
    doSomething { 
        println("Hello world!")
    }
}

In doSomething(), we call the lambda function inside of a coroutine, so another execution context. This non-local control flow is not allowed for normal lambda functions. But, with the crossinline keyword that is possible. You will actually get a warning, once this situation happens to you.

4. Typealias

Typealias is an awesome keyword you can use to create alias names for complex types to simplify your code:

typealias Matrix = List<List<Int>>

fun printMatrix(matrix: Matrix) {
    for(row in matrix) {
        println(row)
    }
}

The complex type List<List<Int>> represents a matrix, but calling it just Matrix would be so much clearer. And in this case, we also don’t want to create a separate class, since it’s only about a type.

5. Reified

In Kotlin, generic type information is only available during runtime. That means, when we write a generic function, we don’t know the type of the generic parameter inside the function body. But sometimes, we just need to know that type. In that case, we can make the generic function an inline function and add the reified keyword in front of the generic type parameter. Since this function is now inlined, the type is already clear at compile time:

fun <T> printType() {
    // doesn't work!
    println(T::class.simpleName)
}

inline fun <reified T> printType() {
    // works!
    println(T::class.simpleName)
}

6. Tailrec

Recursion can be beautiful. But it’s also not that performant because all these recursive function calls must be kept in memory as long as the recursion doesn’t stop. Instead, you should always use loops, if you can. But, in Kotlin, we have the opportunity to use the tailrec keyword. It can be used for tail recursive functions (functions, that have a single recursive call at the end of the return statement). The compiler will then figure out a way to rewrite this function as a function with a loop. So in the end, you can use a concise recursion function, but you still don’t suffer from performance issues. Take a look:

tailrec fun factorial(number: Long, accum: Long = 1): Long {
    val result = number * accum
    return if(number <= 1) result
    else factorial(number - 1, result)
}

Of course, not every recursive function can be written as a tail recursive function. But simple functions like factorial can. Important is that you have this single recursive call at the end.

7. Infix

With the infix-keyword, we can create functions that are applicable in this infix style I’m sure you’ve seen before:

infix fun Long.toThePowerOf(x: Int): Long {
    var result = 1L
    for(i in 1..x) result *= this
    return result
}

fun main() {
    println(2L toThePowerOf 10)
}

You don’t have to use it in this way though, you can also use it just as a normal extension function. But, using these infix functions can sometimes make your code much more readable.

6 Comments

  1. HunnyArora

    Hey I read This Post and Shared to my more programming friends. I just loved your post. Keep sharing this type of helpful content.
    From Hunny Arora

  2. Miguel

    I’m still a beginner to understand most of the keywords. Would be nice if you could add an intermediate Kotlin course adding this kind of material, and getting deeper in methods like pow, chunked, reverse.
    Anyways, thanks for such a great content!!

Submit a Comment