Kotlin Inline Function, Reified Parameters

Filed Under: Kotlin
kotlin-inlined-function-output-1

In this tutorial, we’ll be looking into Kotlin inline function. We’ll follow that with Reified Type Parameters.

Kotlin inline functions

We’ve discussed Kotlin Higher Order Functions and Lambda Expressions before.
They’re super useful in passing functions as parameters. But these functions are objects which have there own callbacks and subsequently memory allocations. Let’s understand how these functions passed as parameters internally work through an example.


fun main(args: Array<String>) {

    println("Hey how are you doing")
    sampleFunction("JournalDev.com", ::println)
}

fun sampleFunction(str: String, expression: (String) -> Unit) {
    println("This is Kotlin Inline Functions Tutorial")
    expression(str)
}

sampleFunction when run passes println as the parameter. Now Kotlin is a JVM based language so everything is converted into bytecode. Let’s look at the bytecode of the above code by going to Tools | Kotlin | Show Bytecode.

kotlin normal bytecode

The main part expression.invoke(). Invoking the lambda expression (println) would create an additional call and hence memory. The invoke method looks like this in Java:


expression(new Function() {
        @Override
        public void invoke() {
         //println statement is called here.

        }
    });

Now if we call multiple functions as parameters each of them would add up to the method count and have a HUGE impact on the memory and the performance.

Inline Functions to the rescue!

Inline functions flatten the function calls by providing the function body and everything to the calling function at runtime. We need to add the inline modifier to do so.
So the sampleFunction above would look like the below code when the sampleFunction is called.


inline fun sampleFunction(str: String, expression: (String) -> Unit) {
    print("This is Kotlin Inline Functions Tutorial")
    expression(str)
}

inline keyword copies the function to the call site. This saves the additional object creation to invoke the parameter function thus saving memory for you.

Let’s look at the bytecode now by decompiling.
kotlin inline function bytecode

Take note: The println lambda expression is expanded in the main function itself in the form of System.out.println. NO more additional calls needed.

Why not make every function inlined?

  • Inlining a function copies the code into one place thereby increasing the generated code. It should be avoided when the function to be invoked through parameter has a big code already.
  • Also, inlined functions can’t access private members of the enclosed class. You’ll have to set them as internal

An example using inline functions is given below:


fun main(args: Array<String>) {
    normalFunction()
}

fun normalFunction() {
    println("This is normal function.")
    inlineFunctionExample({ println("Inlined Functions")},{ println("Instead of object creation it copies the code.")} )
}

inline fun inlineFunctionExample(myFunction: () -> Unit, another: () -> Unit  ) {
    myFunction()
    another()
    print("Finally it's working fine!")
}

In the above code, we’ve passed many lambda expressions.
All of these would be copied at runtime.

Following is the generated bytecode in our IntelliJ:
kotlin inline keyword

All the println lambda calls are flattened in the normalFunction itself. The output takes lesser memory.

kotlin inlined function output

Inline Allows Non-Local Control Flow

With inline functions, you can return from the lambda expression itself and it’ll exit the function in which inline function was called. The below snippet demonstrates the same.


fun main(args: Array<String>) {
    normalFunction()
}

fun normalFunction() {
    println("This is normal function.")
    inlineFunctionExample({ println("Inlined Functions")
        return},{ println("Instead of object creation it copies the code.")} )

    println("This is normal function closing")
}

inline fun inlineFunctionExample(myFunction: () -> Unit, another: () -> Unit  ) {
    myFunction()
    another()
    print("Finally it's working fine!")
}

The output that gets printed is :

kotlin inline function non local control flows

As you can see, the inlined function is exited as well as its enclosing function.
We cannot return from lambda expressions that are a part of normal functions(non inline functions).

To prevent this, we can mark the lambda expression as crossinline. It’ll throw a compiler error if it sees a return statement inside that lambda expression.


fun normalFunction() {
    println("This is normal function.")
    inlineFunctionExample(
    { println("Inlined Functions")
        return //compiler error here
    },
    { println("Instead of object creation it copies the code.")}
    )

    println("This is normal function closing")
}

inline fun inlineFunctionExample(crossinline myFunction: () -> Unit, another: () -> Unit  ) {
    myFunction()
    another()
    print("Finally it's working fine!")
}

noinline

noinline modifier is used to set expressions not to be inlined in the call.


fun main(args: Array<String>) {
    normalFunction()
}

fun normalFunction() {
    println("This is normal function.")
    inlineFunctionExample({ println("Inlined Functions")},
            { println("Instead of object creation it copies the code.")} )

    println("This is normal function closing")
}

inline fun inlineFunctionExample(myFunction: () -> Unit, noinline another: () -> Unit  ) {
    myFunction()
    another()
    print("Finally it's working fine!")
}

anothernoinline expressions don’t support non-local control flow. Setting a return statement in them would throw a compiler error.

Inline Properties

Kotlin inline keyword is allowed on properties too. Just like inline functions, it’ll copy the inline properties accessor methods to the call site.
Inline properties cannot have a backing field.


fun main(args: Array<String>) {
    print(x)
}

var i = 10
inline var x: Boolean
    get() = i == 11
    set(x) { x}

We can set inline separately on the get and set methods too.

Reified Type Parameters

The type of a parameter cannot be retrieved in code since it’s erased at runtime. For inline functions though it is possible using Reified.
To retrieve the type of a parameter, we can set a reified modifier against it.
Why does this work?
Inline functions copy the complete function at runtime, hence the type of the parameters is also available provided we’ve set reified against it.


fun main(args: Array<String>) {
    getT<Double>()
}

inline fun <reified T> getT() {
    print(T::class)
}

The following output gets printed:

kotlin inline function reified parameters

That’s all for kotlin inline functions tutorial, we will look into more Kotlin features in future tutorials.

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages