Usage and detailed explanation of let, apply, also, run, with in Kotlin inline function syntax

1. Introduction

The grammar of kotlin is very strange. Today we will introduce several inline functions that are frequently used in the project.

2. What is an inline function?

The semantics of an inline function  is simple: copy and paste the function body at the function call  . There is no difficulty in using it,  inlinejust modify the function with keywords.

grammar:

inline fun function name(){
}

introduce:

        Decorate it with inline at the front of the normally defined function, no matter where you call the inline function, the method in the function body will be inserted into the call.

Actual combat and analysis:

class TestInline {

    inline fun log(msg: String) {
        System.out.println("show log=${msg}")
    }
    
    fun printLog() {
        log("I am working")
    }
}

fun main() {
    val  tt=TestInline()
    tt.log("aaaa")

}

The result after kotlin compilation:

 

 

analyze:

        Through the comparison between the source code of kotlin and the translation, it is found that as long as the inline function is called, the code of the inline function body will be copied to the calling place.

        Through the above comparison, we can roughly understand the role of the inline function. In fact, according to my analysis, if the body of the inline function is large and is referenced everywhere, it will compile a lot of redundant code. What is the difference between this method and the extraction and calling of the method? benefit? At present, I don't find anything special about this method. The only advantage is that it is easier to read in the result after kotlin decoding.

3. Introduction and use of frequently used let, apply, also, run, with

let, apply, also, run, and with are all inline functions, but they are different by use. Next, we will make detailed analysis and introduction

1、let

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

It can be analyzed from the source code that there are two generic types, one is itself and the other is the result

So the return value of the call is this, itself, and the return value can be of any type. A common usage is to pass the object

aa?.let this execute.

Two commonly used:

1. It is necessary to use the object itself aa?.let{}, so as to avoid the null judgment of the variable

    var aa: String? = "text-"
    //创建TestKKK对象,aa作为变量复制,返回tt
    val aaa = aa.let {
        val tt=TestKKK()
        tt.name=it
        tt
    }

    //返回aa的长度
    val length=aa?.let {
        it.length
    }

2.apply

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

The usage of apply is to return a variable of the same type as the calling parameter.

Common usage:

1. Initialization

    tt?.apply {
        tt = TestKKK()
        tt!!.name = "nodify"
        tt
    }

2. Modify variable parameters

    tt?.apply {
        tt!!.name = "nodify"
        tt
    }

3.also

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

The syntax of also is similar to that of apply. It accepts itself in the high-order function of the block. However, the block does not accept the return value, that is to say, you cannot do the initialization work through also. If a pair is null, then when using also, a null pointer will be reported

    var talso: TestKKK? = null
    talso=talso?.also {
        talso=TestKKK()
        talso!!.name = "0000"
        talso
    }
    Log.log("also=${talso!!.name}")

 So, that is to say, the memory address of the variable itself does not accept the modification of the also body, and also can only modify the attributes of the variable.

4.run

public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

The usage of run is more casual. It returns any type, even if it is null, it can execute the return result.

    var trun: TestKKK? = null
    val run1 = trun.run {
        "Hello"
    }

    Log.log("run1 is String=${run1}")
    val run2 = trun.run {
        trun = TestKKK()
        trun!!.name = "run"

        trun
    }
    Log.log("run2 is TestKKK=${run2!!.name}")

The return type depends on the parameters of the last line of the block code block. If the last line has a return value, it is the type of return

5.with

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

The usage of with is to accept a parameter and return the arbitrary parameter type. The accepted parameters will be the parameters of the block function body.

    val with = with(run3) {
        run2.name = run3.toString()
        run2
    }
    Log.log("with is TestKKK=${with.name}")

Four. Summary

        Through the above learning, from the beginning of what the inline function is to how to define it, and the change of the function body after decoding, it has been popularized. The last is our common let, apply, also, run, with syntax usage.

        Special attention here is the usage of also, since also cannot do object initialization in the block body, so in the work, try to use the block as little as possible to avoid null pointers.

        If you need to initialize, you can use apply, if you use the current variable to return any type, use with, run and let.

apply: Do not use? to do empty interception, especially in the initialization, otherwise the initialization will never be executed

5. Return type

1. Return itself: apply and also, return this, but if also is null, the return is null, even if you initialize the block, it is useless

2. Other return types depend on the last line of code in the block. If the last line is a variable, or the get of an attribute or a method has a return value, then there is a return value, otherwise it is unit.

Guess you like

Origin blog.csdn.net/qq36246172/article/details/131954156