Kotlin-3-scope function summary (run with let apply also)

There are some standard library functions in kotlin - run, with, let, apply, and also. For the initial learning process, it is always silly and unclear, so I made this record.

The above standard library functions can be divided into run{}, T.run(), with(), T.let(), T.apply(), T.also() in turn, and they all provide internal functions for the caller scope, so they are also called scoped functions.

Before recording these scope functions, let's briefly learn {}

{} : In kotlin, only {} is used as the scope of the function body.

fun main(args: Array<String>) {
    val result = {
        println("hello this is {block}")
        1
        "MonkeyKing"
    }

    println(result)          //Function0<java.lang.String>

    println(result())        //hello this is {block}
                             //MonkeyKing
}
这么看的话,kotlin把只有{}的整体当作了一个带返回值的函数体,返回值为最后一行代码的执行结果
而我们知道,一般的函数体定义为 val result = fun(){}

fun main(args: Array<String>) {
    val result =fun() {
        println("hello this is {block}")
        1
        "MonkeyKing"
    }
    println(result)         //Function0<kotlin.Unit>

    println(result())       //hello this is {block}
                            //kotlin.Unit
}
通过比较可以知道,加了函数关键字fun定义后,也就必须规定函数返回值类型(不规定则为Unit)
而且,函数有一个很明显的特点,不调用函数则函数体内部不会被执行。

run() : A code block with a return value, and the code block will be executed in order, and the returned result is the execution result of the last line, otherwise it is Unit.

fun main(args: Array<String>) {
    println("step1")
    val result = run {
        1
        println("step3")
        "MonkeyKing"
    }
    println("step2")
    println(result.javaClass)
    println(result)
//    println(result())//不是函数直接报错
}
log:
step1
step3
step2
class java.lang.String
MonkeyKing

源码分析
/**
 * Calls the specified function [block] and returns its result. 
 * 调用具体的方法[block]并返回结果
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)//面向编译器,高速编译器我要执行一遍lamdba表达式
    }
    return block()//执行lamdba表达式,并返回结果
}

T.run() : Pass itself into the lamdba expression, and return the execution result of the last line at the same time.

fun main(args: Array<String>) {

    val name = StringBuffer("MonkeyKing")
    val result = name.run {
        println(this)                       //MonkeyKing  this就是name传进来的别名,作用等同于name,修改了内容name也会同步修改
        this.append(" SunWuKong")
        println(this)                       //MonkeyKing SunWuKong
        "齐天大圣"
    }
    println("result=$result")               //齐天大圣
    println("name=$name")                   //MonkeyKing SunWuKong
}

源码分析:和run{}相差不大,也是告诉编译器执行一遍,返回执行结果

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

with(T) : I want to compare other scope functions. The remarkable feature of with is that it is an inline function, but not an extended function. You can treat with as a slightly special function. The function name is with, and the parameters passed in are in the function The alias inside the body is this, and the return value is the result of the lamdba expression.

fun main(args: Array<String>) {

    var name = "Monkey king"
    val result = with(name) {
        println("this=$this")           //this=Monkey king
        "hellokotlin"
    }
    println("name=$name")               //name=Monkey king
    println("result=$result")           //result=hellokotlin
}

源码分析:不是拓展函数,只是内联
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

Comprehensive comparison, T.run() and with(T) are very similar in function, except that run is an extension function of T and with is not, the others are basically the same, but sometimes it is more inclined to use T.run()

fun main(args: Array<String>) {

    var name = "MonkeyKing"
    name.run {
        println(this)
    }
    with(name) {
        println(this)
    }
    println("当T可能为null时")

    var myName: String? = null
    myName?.run {//外部拦截  this:String
        println("我不可能为null Myname=$this")
        println(this.length)
        println(this.endsWith("a"))
    }
    with(myName) {//会作为传进来 this:String?
        println(this)
        println(this?.length)
        println(this?.endsWith("a"))
    }

}

这样一比较,run()就优雅多了。

T.let() ; Call the let function, pass itself in at the same time, mark it with it, and return it as the execution result of lamdba, let and run are very similar, one alias is it, the other is this, T in run is more like It is in the context of the T object, so some methods can be omitted. The T in let is more inclined to the external processing of the object, such as the add operation of arraylist. At the same time, let can also change the alias, while run can only use this.

fun main(args: Array<String>) {

    val lists = ArrayList<Int>()
    var result = lists.let { //it:Array<Int>
        it.add(23)
        it.add(22)
        it.add(21)
        it.add(20)
        "MonkeyKing"
    }
    lists.let { lllist ->//lllist:Array<Int>
        lllist.add(19)
        lllist.add(18)
        lllist.add(17)
    }
    println("size=${lists.size}")    //7
    println("result=$result")        //MonkeyKing

}

源码分析 T的拓展函数 只执行一遍 把自身传进去
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
上面的作用域函数只返回lamdba表达式的最后一句的执行结果,后面的apply和also则是返回T本身(A开头的是返回本身,这就很好记了)。apply和also因为是返回调用者T本身,所以在链式调用中就极其的方便。

T.apply() : Pass itself into the expression, marked with this, and return T itself.

fun main(args: Array<String>) {

    val lists = ArrayList<Int>()
    var result = lists.apply {
        this.add(23)
        this.add(22)
        this.add(21)
        this.add(20)
        "MonkeyKing"
    }
    println("size=${lists.size}")           //4
    println("result=$result")               //result=[23, 22, 21, 20]
    println(result == lists)                //true
}

源码分析:
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()                     //执行函数表达式
    return this                 //本身
}

T.also() : Similarly, the difference between also and apply lies in the this passed in by apply, while it is passed in by default in also (can be modified manually)

fun main(args: Array<String>) {

    val lists = ArrayList<Int>()
    var result = lists.also {
        it.add(23)
        it.add(22)
        it.add(21)
        it.add(20)
        "MonkeyKing"
    }
    lists.also { itt ->
        itt.add(10)
        itt.add(11)
        itt.add(12)
    }
    println("size=${lists.size}")           //size=7
    println("result=$result")               //result=[23, 22, 21, 20, 10, 11, 12]
    println(result == lists)                //true
}

源码分析
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)     //传入调用者本身
    return this     //返回调用者本身
}

The general introduction of the scope function is finished. To sum up, in the end, it needs to be used more to get familiar with it.

function Whether to expand the function return value object import
run() no lamdba execution result none
T.run() yes lamdba execution result this
T.let() yes lamdba execution result it
with(T) no lamdba execution result this
T.apply() yes T this
T.also() yes T it

Guess you like

Origin blog.csdn.net/sunguanyong/article/details/125610061