kotlin的run、apply、let、also和with的再次对比说明

发布时间 2023-12-17 17:28:01作者: LCAC
一、这六个的函数
// 扩展函数run
public inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}
// 扩展函数let
public inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}
// 扩展函数apply
public inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}
// 扩展函数also
public inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}
// 全局函数run
public inline fun <R> run(block: () -> R): R {
    return block()
}
// 全局函数with
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

 

 

二、各个函数的作用和对比

1、扩展函数run和let:let相对run lambda增加了将this传入。其他都是一样的可以使用this,会返回lambda的计算结果。

习惯上,经常会将let与?. 一起使用(当然用run也是可以的,只是说习惯上)

   val listWithNulls: List<String?> = listOf("A", null)
   for (item in listWithNulls) {
       item?.let { println(it) } // Prints A and ignores null
   }

当然我们如果出现函数链的方式将run和let交替使用会使代码更清晰

val list = listOf("Apple", "Banana", "Cherry")
list.maxByOrNull { it.length }?.let { it.toLowerCase() }?.run { println(this) }

 

2、扩展函数apply和also:also相对apply lambda增加了将this传入。其他都是一样的可以使用this,并且函数的返回是this。注意lambda的返回结果均会被忽略。同样出国出现函数链的方式将apply和also交替使用会使代码更清晰

apply更适合改变对象自身,用于一些初始化的操作;also则更加偏向于在处理完对象后还需要做其他相关的操作。
// 创建foo之后,对成员函数进行初始化
val foo = Foo().apply {
    bar = 1
    baz = 2
}

// 创建list之后,进行后续的相关的操作
val list = mutableListOf("item").also { println("$it has been created") }

 

3、全局函数run和with:run只接收一个无参的lambda表达式,with则接收一个对象参数和该对象的扩展函数lambda。注意这两个函数均返回对应lambda值的计算结果

run:可以用来创建一个新的局部作用域。这在需要限制一些临时变量的可见性或者需要计算并返回一些结果时非常有用。

val result = run {
    val x = 1
    val y = 2
    x + y  // 这个表达式的值将被返回
}

with:在with的lambda内部,可以用this表示传入的对象参数。它提供一种简洁的方式来调用同一个对象的多个方法和属性。

with的优点:不仅可以使代码更简洁,还可以把对象的一系列操作包装起来,形成一个独立的逻辑单元,这会使代码更易于阅读和维护。同时它也可以帮助你避免重复地输入同一个对象名。

注意:with接收的对象是要非空的对象,当可能为空的话最好不要用with