kotlin中的let、also、with、apply、run

一直对这几个函数有些迷惑,一会用这个,一会用那个,这次好好弄明白一下!

1 let

(1)源码解析

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
  • 对象T的扩展函数,且为内联函数,减少函数调用开销。
  • contract 是 Kotlin 用于定义函数契约的一种机制。函数契约可以向编译器提供关于函数行为的一些额外信息,帮助编译器进行更准确的代码分析、优化以及处理一些边缘情况等。
  • callsInPlace(block, InvocationKind.EXACTLY_ONCE) 这个语句表示向编译器声明,传入的函数参数 block 在 also 函数执行过程中会被调用,并且恰好调用一次(InvocationKind.EXACTLY_ONCE 表示精确地调用一次)。这样的契约声明可以让编译器在一些涉及到函数参数是否被调用、调用次数等相关的代码检查和优化时有更明确的依据,例如,在某些内联函数展开的场景下,对于这种有调用次数约定的函数参数,编译器可以更好地处理其相关的逻辑
  • block(this) 调用传入的函数 block,并且将当前对象的实例T作为参数传递给 block
  • 最后返回block函数的运行结果

(2)用法

主要用来判空,需使用it,且返回传入函数的返回

class Person(var name: String, var age: Int) {
    fun printFun() {
        println("name: $name, age: $age")
    }
}
fun main() {
    val person = Person("xiaohong", 25).let {
        it.name = "xiaoming"
        it.printFun() // name: xiaoming, age: 25
        "hello"
    }
    println(person) // hello
}

不返回本身,返回传入的函数的最后一行。

2 also

(1)源码解析

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}
  • 对象T的扩展函数,且为内联函数,减少函数调用开销。
  • block(this) 调用传入的函数 block,并且将当前 对象的实例T作为参数传递给 block
  • 最后返回对象本身

(2)用法

要使用it访问

class Person(var name: String, var age: Int) {
    fun printFun() {
        println("name: $name, age: $age")
    }
}

fun main() {
    val person = Person("xiaohong", 25).also {
        it.name = "xiaoming"
        it.printFun() // name: xiaoming, age: 25
    }
    person.printFun() // name: xiaoming, age: 25
}

可见,最后返回了对象本身 ,且使用时需要用it调用

3 apply

(1)源码解析

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
  • 同also一样,返回了对象本身
  • ⚠️注意一点,这里的block是T对象的扩展函数,即T.(),这样也是可直接在block函数中访问T对象中的数据,而无需it

(2)用法

无需it访问,且返回对象本身

class Person(var name: String, var age: Int) {
    fun printFun() {
        println("name: $name, age: $age")
    }
}
fun main() {
    val person = Person("xiaohong", 25).apply {
        name = "xiaoming"
        printFun() // name: xiaoming, age: 25
        "hello"
    }
    person.printFun() // name: xiaoming, age: 25
    println(person) // Person@3ac3fd8b
}

可见,没有使用it,直接访问的name属性,且没返回"hello",是返回的对象本身

4 run

(1)源码解析

public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
  •  看源码可知,与apply的差别主要在于返回的东西,apply是返回对象本身,run是返回传入函数的返回,或者说传入函数的最后一行

(2)用法

与apply一样,既然传入的是对象的扩展,那么也不需要it访问,可直接访问对象属性

class Person(var name: String, var age: Int) {
    fun printFun() {
        println("name: $name, age: $age")
    }
}
fun main() {
    val person = Person("xiaohong", 25).run {
        name = "xiaoming"
        printFun() // name: xiaoming, age: 25
        "hello"
    }
    println(person) // hello
}

5 with

与前面四个不一样了,前面四个都是函数的扩展函数,而with确实一个原型函数,是直接调用的

(1)源码解析

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
  • with不是T.with,因此不是扩展函数
  • 有两个参数,一个为传入对象T,还有一个是传入的函数参数,或者说lambda表达式,其是做为T对象的扩展函数
  • 返回传入函数的返回值

(2)用法

直接使用with,先传入对象,再传入对T操作的lambda表达式,最后返回lambda表达式的返回值

class Person(var name: String, var age: Int) {
    fun printFun() {
        println("name: $name, age: $age")
    }
}
fun main() {

    val person = with(Person("xiaohong", 25)) {
        name = "xiaoming"
        printFun() // name: xiaoming, age: 25
        "hello"
    }
    println(person) // hello
}

6 总结

这几个函数都是操纵某个对象的函数,但操作方式以及返回值有差别。

函数的扩展函数:let(it调用,返回传入函数的返回值)、also(it调用,返回对象本身)、apply(this调用,返回对象本身)、run(this调用,返回传入函数的返回值)

原型函数:with(需传入对象,this调用,返回传入函数的返回值)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值