with
函数接收两个参数:第一个参数可以是一个任意类型的对象,第二个参数是一个Lambda表达式。with
函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回。示例代码如下:
val result = with(obj) {
// 这里是obj的上下文
"value" // with函数的返回值
}
那么这个函数有什么作用呢?它可以在连续调用同一个对象的多个方法时让代码变得更加精简,下面我们来看一个具体的例子。
比如有一个水果列表,现在我们想吃完所有水果,并将结果打印出来,就可以这样写:
val list = listOf("苹果","香蕉","梨","火龙果","芭蕉","椰子")
val builder = StringBuilder()
builder.append("开始\n")
for (fruit in list){
builder.append(fruit).append("\n")
}
builder.append("结束\n")
println(builder.toString())
仔细观察上述代码,你会发现我们连续调用了很多次builder
对象的方法。其实这个时候就可以考虑使用with
函数来让代码变得更加精简,如下所示:
val builderWith = with(StringBuilder()) {
append("开始\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("结束").append("\n")
toString()
}
println(builderWith)
这两段代码的执行结果是一模一样的,但是明显第二段代码的写法更加简洁一些,这就是with
函数的作用。
run
函数的用法和使用场景其实和with
函数是非常类似的,只是稍微做了一些语法改动而已。首先run
函数通常不会直接调用,而是要在某个对象的基础上调用;其次run
函数只接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文。其他方面和with
函数是一样的,包括也会使用Lambda表达式中的最后一行代码作为返回值返回。示例代码如下:
val result = obj.run {
// 这里是obj的上下文
"value" // run函数的返回值
}
那么现在我们就可以使用run
函数来修改一下吃水果的这段代码,如下所示:
val builderRun = StringBuilder().run {
append("开始\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("结束").append("\n")
toString()
}
println(builderRun)
总体来说变化非常小,只是将调用with
函数并传入StringBuilder
对象改成了调用StringBuilder
对象的run
方法,其他都没有任何区别,这两段代码最终的执行结果是完全相同的。
apply
函数和run
函数也是极其类似的,都要在某个对象上调用,并且只接收一个Lambda参数,也会在Lambda表达式中提供调用对象的上下文,但是apply
函数无法指定返回值,而是会自动返回调用对象本身。示例代码如下:
val result = obj.apply {
// 这里是obj的上下文
}
// result == obj
那么现在我们再使用apply
函数来修改一下吃水果的这段代码,如下所示:
val builderApply = StringBuilder().apply {
append("开始\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("结束").append("\n")
}
println(builderApply.toString())
注意这里的代码变化,由于apply
函数无法指定返回值,只能返回调用对象本身,因此这里的result
实际上是一个StringBuilder
对象,所以我们在最后打印的时候还要再调用它的toString()
方法才行。