It has been a long time since I wrote my blog last time. Since I changed my job in the middle, I haven’t updated my blog for a long time. I am also busy with my new company project. Although kotlin is not mandatory for company projects, BUT I love Kotlin syntax, the following is my usual reading of some kotlin source code, encountered related problems summary, but also to share my ideas. Ok, so here’s where this blog post comes in:

Higher-order functions

For those of you who have used Kotlin, you know what a higher-order function is. To better illustrate this blog post, I’d like to tell you what a higher-order function is.

fun runSomething(param: String, run: () -> Unit) {
    println("param:$param")
    run.invoke()
}
Copy the code

Unlike normal Java functions, where the argument to a Java function must be a variable, in Kotlin, the function expression can be an expression. The run argument above is actually a Lambda expression.

runSomething("param1", {
    println("I am the second argument to the higher-order function.")})Copy the code

In Kotlin, we have this syntax: if the last argument to a function is a Lambda expression, we can put the Lambda expression after the parentheses, so we have the following syntax:

runSomething("param1"){
    println("I am the second argument to the higher-order function.")}Copy the code

The above runSomething is a function of a higher-order function, which can be passed as a Lambda expression as an argument to the function.

Extend functions, extend attributes

Extension functions generally refer to the extension of class methods, variables, the following also through the example to see if the extension of class methods, variables, because it is the extension of the class, I define a kotlin class:

class Animal
Copy the code

An animal class, then extends eat(the method to eat) and name(the attribute of the name):

fun Animal.eat(eat: () -> Unit) {
    eat.invoke()
}

val Animal.name: String
    get() {
        return "Panda"
    }
Copy the code

So when we use it, we can use it like this:

val animal = Animal()
animal.eat {
    println("I eat bamboo.")
}
val animalName = animal.name
Copy the code

Having said the higher-order functions and extension functions, let’s talk about the inline functions in higher-order functions.

Inline functions (inline)

Kotlin: Inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline inline The runSomething function is not inline. The runSomething function is not inline. The runSomething function is not inline.

public final class Unit13Kt {
    public static final void runSomething(@NotNull String param, @NotNull Function0<Unit> run) {
        Intrinsics.checkParameterIsNotNull(param, "param");
        Intrinsics.checkParameterIsNotNull(run, "run");
        System.out.println("param:" + param);
        run.invoke();
    }

    public static final void main(@NotNull String[] args) {
        Intrinsics.checkParameterIsNotNull(args, "args"); // The second argument is main.1.INSTANCE object runSomething("param1", main.1.INSTANCE); } // Kotlin automatically generates a class final Class Unit13Kt$mainThe $1Public static final Unit13Kt extends Lambda implements Function0<Unit> {// Public static final Unit13Kt$mainThe $1 INSTANCE = new Unit13Kt$mainThe $1(a); Unit13Kt$mainThe $1() {
        super(0);
    }

    public final void invoke() {
        System.out.println("I am the second argument to the higher-order function."); }}Copy the code

INSTANCE Unit13Kt$main$1 Unit13Kt$main$1 Unit13Kt$main$1 Unit13Kt$main$1 Unit13Kt$main$1 is a class that kotin automatically generates. It inherits from the Lambda class and implements the Function0

interface. And you can test it by looking at lambda parameter passing, but I won’t test it here. The statement inside its Invoke method is the body of the method above that defines the lambda expression. Let’s take a look at a higher-order function with the inline keyword. It’s easy to use the inline keyword in front of the runSomething method:

inline fun runSomething(param: String, run: () -> Unit) {
    println("param:$param")
    run.invoke()
}
Copy the code

Let’s focus on the decompiled code generated under inline:

public final class Unit13Kt {
    public static final void runSomething(@NotNull String param, @NotNull Function0<Unit> run) {
        Intrinsics.checkParameterIsNotNull(param, "param");
        Intrinsics.checkParameterIsNotNull(run, "run");
        System.out.println("param:" + param);
        run.invoke();
    }

    public static final void main(@NotNull String[] args) {
        Intrinsics.checkParameterIsNotNull(args, "args");
        System.out.println("param:" + "param1");
        System.out.println("I am the second argument to the higher-order function."); }}Copy the code

Inline does not change the method itself, only changes the calling point. The inttance variable is not generated as the parameter runSomething, but the method body of the higher-order function is directly moved to the calling point to reduce object creation.

summary

Believe that, after seeing the decompiled code above everyone had a preliminary understanding for the inline, and what in be used actually need to pay attention to, if the higher-order functions use frequency is quite high, so generally need to define the higher-order functions as the inline type, reduce unnecessary object creation, whereas the higher-order functions use is not very frequent, There is no need for higher-order functions to be inline. After all, inline causes program efficiency problems, so to speak, inline is to trade efficiency for memory problems.

noinline

The word “noinline” is the opposite of “inline”. It must be used with “inline”.

testNoInline({
    println("Lambda expression for run")
}, {
    println("Lambda expression of result")
    "result"
})
Copy the code

Note that the return value of the lambda expression is the last statement, so I added the return value of “result”. Let’s focus on decompiled code:

public static final void testNoInline(@NotNull Function0<Unit> run, @Nullable Function0<String> result) {
    Intrinsics.checkParameterIsNotNull(run, "run");
    run.invoke();
    if(result ! = null) { String str = (String) result.invoke(); } } public static final void main(@NotNull String[] args) { Intrinsics.checkParameterIsNotNull(args,"args");
    Function0 result$iv = main.2.INSTANCE;
    System.out.println("Lambda expression for run");
    if (result$iv! = null) { String str = (String) result$iv.invoke(); }}Copy the code

As you can see from the decompression, if we add nullable modifier to the lambda expression, we will be prompted to add the noinline keyword to the lambda expression, because the decompression code requires a non-null criterion. Without the noinline keyword, there will be no instance variable. The result lambda expression should be noinline, while the run expression should be inline.

summary

Noinline is used on lambda expressions that do not need to be inline. For example, the result expression above needs to be defined as noinline. If noinline is defined, the expression needs to generate the corresponding instance variable processing.

crossinline

The crossinline modifier is a syntactic specification for inline and noinline. Crossinline must also be used with inline as an example:

inline fun testCrossinline(run: () -> Unit, crossinline run1: () -> Unit) {
    run.invoke()
    run1.invoke()
}
Copy the code

Defines a run1 that is of type Crossinline, and then looks where the call is made:

crossinline

The main method can only return the current function, not directly return the outer main method.

public static final void testCrossinline(@NotNull Function0<Unit> run, @NotNull Function0<Unit> run1) {
    Intrinsics.checkParameterIsNotNull(run, "run");
    Intrinsics.checkParameterIsNotNull(run1, "run1");
    run.invoke();
    run1.invoke();
}

public static final void main(@NotNull String[] args) {
    Intrinsics.checkParameterIsNotNull(args, "args");
    System.out.println("Lambda expression for run");
}
Copy the code

Crossinline can be used under certain conditions that do not want to execute a lambda expression, so the current method of return is not to execute the expression.

summary

Crossinline is useful for not executing lambda expressions under certain conditions by returning the current method without executing the expression. This is very rare in the Kotlin source code, and if you see the use of Crossinline in the Kotlin source code, you can check to see if this is the case.

In fact, kotlin’s higher-order functions, extension functions, extension attributes, and inline functions can be seen in many places. The kotlin standard functions we use, such as let, run, apply, also, and with, are all used in the extension of functions and the use of inline. If I have time, I will explain the functions of let to fill in.

conclusion

  • Higher-order function: A function is a higher-order function by taking an expression as a parameter
  • Extend methods, extend properties: To achieve the effect of extension by redefining the form of a function or variable without changing the original class.
  • Inline: An inline function is a representation of an inline function. If a function is inline, the lambda expression does not generate extra lambda objects and moves the method body directly to the calling point. This is used when the method is called frequently
  • Noinline: is the opposite of inline, but needs to be used with inline functions. If the expression is defined as noinline, an object will be generated, and some logic needs to be operated on this object instead of being defined as a lambda expression of type noinline. The nullable lambda expression above.
  • Crossinline: Used when a method cannot return the outer method directly, but only the current method. Return the current method without executing the expression. This is usually used when the expression is not executed under certain conditions.

Well, it’s time to go to bed. If you have anything to add, feel free to comment below.