Kotlin 高阶函数: let/also/with/run/apply

let

函数签名

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

使用场景

  1. 针对一个可 null 对象统一做判空处理,避免写一些判断 null 的操作
// 如果 object 为 null,则 let 闭包中的方法不会被执行
// 如果 object 不为 null,则 let 闭包中默认的 it 变量为不可选类型
object?.let {
    it.doSomething()
    // 闭包的返回值
    "xxx" 
}
复制代码
  1. 使用 it 替代 object 对象去访问公有的属性 & 方法
// 无论 object 是否为空均会执行,则 let 闭包中默认的 it 变量和 object 保持一致
object.let {
    it.doSomething()
    // 闭包的返回值
    "xxx" 
}
复制代码

使用示例

闭包不为空时执行闭包

block?.let { block(it) }
复制代码

also

函数签名

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    // 闭包的返回值为 this
    return this
}
复制代码

和 let 区别

  • let: 返回值为最后一行
  • also: 返回值为 this

所以在函数没有返回值的情况下,letalso 并无差别。

使用场景

在变量初始化的时候给变量的属性赋值

val object = Object().also {
    it.xxx = "xxx"
    it.doSomething()
}
复制代码

with 函数

函数签名

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

使用场景

调用一个对象的多个方法或者设置多个属性时,可以省去对象名,直接调用

// 闭包里面的变量为 this,而不是 it
with(object) {
    // 这里就相当于 object.doSomething()
    doSomething()
    // 闭包的返回值
    xxx
}
复制代码

run

函数签名

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

作用

结合了 letwith 两个函数的作用

  1. 可以判空
  2. 直接调用,无需 it

使用场景

object?.run {
    doSomething()
}
复制代码

apply

函数签名

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

和 run 区别

  1. run 的返回值为闭包的最后一行
  2. apply 的返回值为 this

使用场景

可以更方便的写链式调用

class Test {
    fun test() = apply {
        doSomething()
    }
    
    fun test1() = apply {
        doSomething()
    }
}

// other class
Test().run {
    test().test1()
}
复制代码

总结

由于 let/also/run/apply 均为 T 的扩展函数,所以均支持链式调用。他们的区别只在于参数和返回值是否相同。

如果抉择

let/run 由于返回值为最后一行,更适用于一次执行。

also/apply 由于返回值为 this,更适用于链式调用。

选择使用 this 还是 it 访问当前对象,一个原则就是:如果使用 this,那么就不要把 this. 写出来,利用 this 关键字可以省略的特性。

特殊点

(T) -> R 可以看成是把 this 当成参数传入闭包中进行处理。

T.() -> R 可以看成是给 T 类型的类扩展了一个 () -> R 的属性,当 receiver.block() 被执行的时候,相当于 T 类型中扩展的 () -> R 类型的闭包被执行了。

详情可以去看一下 kotlin 中闭包以及扩展属性/函数。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享