Some understandings about inline functions in Kotlin

  • 2021-09-16 08:05:18
  • OfStack

Preface

I read a lot of blogs, To understand the meaning of inline, In fact, the most fundamental thing is to copy the code written elsewhere to the method you are executing now, which is equivalent to executing in one method. The method execution of java needs to press the stack out of the stack, right? If it is two or three methods, it is two or three times of pressing the stack out of the stack. In order to save this operation and improve the efficiency of one decision, kotlin has such a function. But think about it again, if it is a super-large function, it is also very troublesome to test it, so this thing needs to be weighed by itself, and it is fundamental to abide by the duty of single 1 and reduce the clutter of code circle.

Understanding of inline function

Conceptually, inline function (inline function) is that the compiler uses the real code implemented by the function to replace every function call. The most direct benefit is to save the overhead of function call, but the disadvantage is to increase the size of the generated bytecode. Based on this, is it necessary for us to define all functions as inline when the amount of code is not very large? Let's explain it in two situations:

Define ordinary functions as inline: as is known to all, Inline optimization has been implemented internally in JVM, It will inline function calls wherever performance can be improved by inline, and compared with manually defining ordinary functions as inline, the implementation of each function will only occur once through JVM inline optimization, thus ensuring the reduction of runtime overhead without increasing the size of bytecode; Therefore, we can conclude that for ordinary functions, we don't need to declare them as inline functions, but leave them to JVM for self-optimization. Defining functions with lambda parameters as inline: Yes, this does improve performance; However, in the process of using it, we will find that it has many limitations. Let's start with the following examples:

inline fun doSomething(action: () -> Unit) {
 println("Before doSomething...")
 action()
 println("After doSomething...")
}

Suppose we call doSomething like this:


fun main(args: Array<String>) {
 doSomething {
  pringln("Hello World")
 }
} 

The above call will be compiled into:


fun main(args: Array<String>) {
 println("Before doSomething...")
 println("Hello World")
 println("After doSomething...")
}

From the results of the above compilation, we can see that both the doSomething function and the action parameter are inlined, which is great. Let's change the calling method:


fun main(args: Array<String>) {
 val action:() -> Unit = { println("Hello World") }
 doSomething(action)
}

The above call will be compiled into:


fun main(args: Array<String>) {
 println("Before doSomething...")
 action()
 println("After doSomething...")
}

The doSomething function is inlined, but the action parameter is not inlined, because lambda passed to doSomething as a functional variable is not available at the call point of the function, and the lambda can only be used normally after doSomething is inlined.

From the above example, let's make a brief summary of when lambda expressions are inlined:

When an lambda expression is passed directly to an inline function as an argument, the code for the lambda expression is replaced directly with the resulting code. When an lambda expression is saved somewhere and passed as a variable to an inline function, the code for the lambda expression will not be inline.

The inline timing of lambda has been discussed above. After digesting it for a moment, let's look at the last example:


inline fun doSomething(action: () -> Unit, secretAction: () -> Unit) {
 action()
 doSomethingSecret(secretAction)
}

fun doSomethingSecret(secretAction: () -> Unit) {
}

Is there a problem with the above example? Yes, the compiler throws an error of "Illegal usage of inline-parameter", because Kotlin stipulates that lambda parameters in inline functions can only be called directly or passed to another inline function, and cannot be used for other purposes; What if we really want to pass an lambda to a non-inline function? We just need to modify the above code as follows:


inline fun doSomething(action: () -> Unit, noinline secretAction: () -> Unit) {
 action()
 doSomethingSecret(secretAction)
}

fun doSomethingSecret(secretAction: () -> Unit) {
}

Quite simply, add the noinline modifier before the lambda parameter that does not need to be inline.

That's all I understand about inline functions. By mastering the operation mechanism of this feature, I believe you can use this feature at the right time, instead of abusing it or abandoning it because of fear.

Summarize


Related articles: