Understanding inline, noinline, and crossinline in Kotlin

In this blog, we are going to learn the inline, noinline, and crossinline keywords in Kotlin. We will understand when to use the inline, when to use noinline and when to use the crossinline in Kotlin depending on our use-case. Most of the time, we do mistake while using these keywords inline, noinline, and crossinline in Kotlin. Let's understand it clearly to use it properly.

What is inline function?

Inline function instruct compiler to insert complete body of the function wherever that function got used in the code.

Advantages of inline function are as follows:

  • Function call overhead doesn't occur.
  • It also saves the overhead of push/pop variables on the stack when the function is called.
  • It also saves the overhead of a return call from a function. Such optimizations are not possible for normal function calls.

Let's see it with an example

fun doSomething() {
    print("doSomething start")
    doSomethingElse()
    print("doSomething end")
}

fun doSomethingElse() {
    print("doSomethingElse")
}

Here, we have the following two functions:

  • doSomething()
  • doSomethingElse()

Both are normal functions. doSomething() function is calling the doSomethingElse() function.

In order to understand it better, let's see the decompiled code

In Android Studio. Go to: Tools -> Kotlin -> Show Kotlin Bytecode, then click the Decompile button.

The decompiled code is as below:

public void doSomething() {
   System.out.print("doSomething start");
   doSomethingElse();
   System.out.print("doSomething end");
}

public void doSomethingElse() {
   System.out.print("doSomethingElse");
}

As we can see here, the doSomething() function is calling the doSomethingElse() function as usual.

Now let's try to add the inline keyword as below in the doSomethingElse() function.

fun doSomething() {
    print("doSomething start")
    doSomethingElse()
    print("doSomething end")
}

inline fun doSomethingElse() {
    print("doSomethingElse")
}

Again, let's see the decompiled code. The decompiled code is as below:

public void doSomething() {
   System.out.print("doSomething start");
   System.out.print("doSomethingElse");
   System.out.print("doSomething end");
}

Now, we can see that the code of doSomethingElse() function is copied inside the doSomething() function. And the doSomething() function is no more calling the doSomethingElse() function.

This is all because we have used the inline keyword.

So, when to make the function inline and when not:

  • When the function code is very small, it's good idea to make the function inline.
  • When the function code is large and called from so many places, it's a bad idea to make the function inline, as the large code will be repeated again and again.

Going deeper with inline

Before that, we need to learn about the Higher-Order Functions and Lambdas in Kotlin. If you know them, it's good to go, if not, you need to refer the below blog post before moving forward.

Understanding Higher-Order Functions and Lambdas in Kotlin

Now, let's take an example with Higher-Order Function and Lambdas

fun doSomething() {
    print("doSomething start")
    doSomethingElse {
        print("doSomethingElse")
    }
    print("doSomething end")
}

fun doSomethingElse(abc: () -> Unit) {
    // I can take function
    // do something else here
    // execute the function
    abc()
}

Again, let's see the decompiled code. The decompiled code is as below:

public void doSomething() {
    System.out.print("doSomething start");
    doSomethingElse(new Function() {
        @Override
        public void invoke() {
            System.out.print("doSomethingElse");
        }
    });
    System.out.print("doSomething end");
}

public void doSomethingElse(Function abc) {
    abc.invoke();
}

Now let's try to add the inline keyword as below in the doSomethingElse() function.

fun doSomething() {
    print("doSomething start")
    doSomethingElse {
        print("doSomethingElse")
    }
    print("doSomething end")
}

inline fun doSomethingElse(abc: () -> Unit) {
    // I can take function
    // do something else here
    // execute the function
    abc()
}

Again, let's see the decompiled code. The decompiled code is as below:

public void doSomething() {
    System.out.print("doSomething start");
    System.out.print("doSomethingElse");
    System.out.print("doSomething end");
}

As, we can see that the code of doSomethingElse function is placed inside the doSomething function.

This is how inline can help us to make the execution fast by avoiding the fuction calls.

Now, as we have learned the inline, we will move on to the noinline and crossinline keywords.

What is noinline?

Assume that we have two lambdas as abc and xyz like below:

inline fun doSomethingElse(abc: () -> Unit, xyz: () -> Unit) {
    // I can take function
    // do something else here
    // execute the function
    abc()
    xyz()
}

But, we do not want to inline both the lambdas abc and xyz, we just want to inline abc, but not the xyz, in this case, we need to use the noinline before the xyz like below:

inline fun doSomethingElse(abc: () -> Unit, noinline xyz: () -> Unit) {
    // I can take function
    // do something else here
    // execute the function
    abc()
    xyz()
}

This way, we can use the noinline to avoid the inlining.

Now, we have understood what is noinline. Let's move to the next one which is crossinline.

What is crossinline?

In order to understand the crossinline, we need to understand "non-local returns".

Let's take an example to understand the non-local returns.

fun doSomething() {
    print("doSomething start")
    doSomethingElse {
        print("doSomethingElse")
        return // notice this return
    }
    print("doSomething end")
}

inline fun doSomethingElse(abc: () -> Unit) {
    // I can take function
    // do something else here
    // execute the function
    abc()
}

Again, let's see the decompiled code. The decompiled code is as below:

public void doSomething() {
    System.out.print("doSomething start");
    System.out.print("doSomethingElse");
}

Here we can see that there is no System.out.print("doSomething end"). As we have added the return inside the lambdas, it allowed the non-local returns and left the code below that.

How to avoid this?

We need to add the crossinline, then it will not allow us the put the return inside that lambdas like below:

fun doSomething() {
    print("doSomething start")
    doSomethingElse {
        print("doSomethingElse")
        // return is not allowed here
    }
    print("doSomething end")
}

inline fun doSomethingElse(crossinline abc: () -> Unit) {
    // I can take function
    // do something else here
    // execute the function
    abc()
}

This is how the crossinline can help us to avoid the "non-local returns".

Now, we have understood all three keywords.

  • inline
  • noinline
  • crossinline

Now we can use these keywords inline, noinline and crossinline properly in Kotlin.

Happy Learning :)

Team MindOrks

Also, Let’s become friends on Twitter, Linkedin, Github, Quora, and Facebook.