Blog Infos
Author
Published
Topics
Published
Topics
1. What are inline functions?

Inlining functions causes the compiler to produce code at call sites instead of producing the function itself. It’s beneficial when you’re using lambdas in your code. It might be a little hard to understand at the moment; look at the example below:

2. Have you ever passed a lambda in your function?

Let’s look at the example to understand what happens under the hood:

fun load(onEnd: () -> Unit) {
    // Load something here
    onEnd()
}

// Use
fun main() {
    load {
        print("Done")
    }
}

You might not know this, but doing so creates an object from this lambda. It’s the same as if you’ve had this written:

interface SingleAction {
    operator fun invoke()
}

fun load(onEnd: SingleAction) {
    // Load something here
    onEnd()
}

// Use
fun main() {
    load(
        object : SingleAction {
            override fun invoke() {
                print("Done")
            }
        }
    )
}

The effect will be the same because passing lambdas comes with a penalty of having to create an object.

It might look okay, but what if you call this function in a loop or multiple places in your codebase? Each call will create a new object, and it might affect your performance.

3. inline keyword to the rescue

What if we inlined our function?

inline fun load(onEnd: () -> Unit) {
    // Load something here
    onEnd()
}

// Use
fun main() {
    load {
        print("Done")
    }
}

Using the function is the same, but here’s what the compiler will produce:

fun main() {
    // Load something here
    print("Done")
}

The entire body function is moved to a place where it’s used. The compiler will produce code that doesn’t even have a load function in it. We aren’t creating any objects because we’re not passing any lambdas.

This way, we can use lambdas to their full potential without having performance issues. But there is a catch…

Inlining functions causes your code to grow. If it’s in more than 1 place, you must repeat the same code so it naturally grows. However, if done right, the payoff in performance will be amazing, especially at megamorphic call sites.

Job Offers

Job Offers


    Senior Android Engineer

    Carly Solutions GmbH
    Munich
    • Full Time
    apply now

    Senior Android Developer

    SumUp
    Berlin
    • Full Time
    apply now

OUR VIDEO RECOMMENDATION

No results found.

Jobs

4. noinline keyword

Another problem is that inlined lambdas can only be called inside inlined functions and passed as inlined arguments. This means a code like this, it’ll throw a compile error:

inline fun load(onHalfLoaded: () -> Unit, onEnd: () -> Unit) {
    fetch(onHalfLoaded /* This causes compilation error */)
    onEnd()
}

fun fetch(onHalfLoaded: () -> Unit) {
    // Load
    onHalfLoaded()
    // Load
}

We can fix it by adding noinline keyword to the passed lambda in load function:

inline fun load(noinline onHalfLoaded: () -> Unit, onEnd: () -> Unit) {
    fetch(onHalfLoaded /* This causes compilation error */)
    onEnd()
}

This way, we still benefit from inlining onEnd and are still able to use fetch . Another way to fix this issue is to inline fetch instead:

inline fun fetch(onHalfLoaded: () -> Unit) {
    // Load
    onHalfLoaded()
    // Load
}
5. Non-local Returns

Normally, we can return only locally in the function’s scope, but because our functions are inlined, we can return outside. For example:

inline fun load(onEnd: () -> Unit) {
    // Load
    onEnd()
}

fun main() {
    load {
        return // The main ends here
    }
    // Loaded is NOTprinted
    print("Loaded")
}

To return in a scope of a function, we can do so with Kotlin Scope annotation:

fun main() {
    load {
        return@load
    }
    // Loaded IS printed
    print("Loaded")
}
6. reified type parameters

Sometimes, you’ll need to access the type of the passed parameter:

fun <T> checkType(data: Any) = data is T // Compilation error

But you can’t because the T type is erased, but what if we inlined it? At a call site, the type isn’t erased! This way, we can create a generic function and still know the type. Here’s the transformed function:

inline fun <reified T> checkType(data: Any) = data is T // Now it works

It allows You to create very generic functions.

If you’ve found this article helpful, please clap so it’s easier for others to see it, and follow me for more!

Based on:

https://kotlinlang.org/docs/lambdas.html?source=post_page—–ccdb6a114478——————————–

This article previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
This is the second article in an article series that will discuss the dependency…
READ MORE
blog
Let’s suppose that for some reason we are interested in doing some tests with…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu