本文讲解 Kotlin 标准函数 let、with、run、apply、also 的用法。
? 官方文档
Kotlin 的标准函数指的是 Standard.kt 文件中定义的函数,任何 Kotlin 代码都可以自由地调用所有的标准函数。
一、let
? let 官方文档
// 示例代码
obj.let { obj2 ->// TODO: 编写具体的业务逻辑
}
作用:调用了 obj 对象的 let 函数,然后 Lambda 表达式中的代码就会立即执行,并且这个 obj 对象本身还会作为参数传递到 Lambda 表达式中。为了防止变量重名,这里将参数名改成了 obj2,但实际上它们是同一个对象。
示例:
创建一个 Study 接口,在接口中定义两个待实现函数:
interface Study {fun readBooks()fun doHomework()
}
创建一个 doStudy() 函数,接收 Study 类型的参数:
fun doStudy(study: Study?) {if (study != null) {study.readBooks()}if (study != null) {study.doHomework()}
}
进行一次 if 判断就能随意调用 study 对象的任何函数,但受制于 ?. 操作符的限制,每次调用 study 对象的函数时都要进行一次 if 判断。
结合使用 ?. 操作符和 let 函数对代码进行优化:
fun doStudy(study: Study?) {study?.let {it.readBooks()it.doHomework()}
}
?. 操作符表示对象为空时什么都不做,对象不为空时就调用 let 函数,而 let 函数会将 study 对象本身作为参数传递到 Lambda 表达式中,此时的 study 对象肯定不为空,我们就能放心地调用它的任意方法了。
二、with
? with 官方文档
// 示例代码
val result = with(obj) {// TODO: 这里是 obj 的上下文// with 函数的返回值"value"
}
作用:它可以在连续调用同一个对象的多个方法时让代码变得更加精简。
示例:
有一个汽车品牌列表,现在想打印出列表中所有汽车品牌的名字,未使用 with 函数的写法:
fun main() {val list = listOf("BMW", "BENZ", "AUDI", "TOYOTA", "HONDA")val builder = StringBuilder()builder.append("Car brand: \n")for (car in list) {builder.append(car).append("\n")}builder.append("Done.")val result = builder.toString()println(result)
}
运行代码,Log 如下:
Car brand:
BMW
BENZ
AUDI
TOYOTA
HONDA
Done.
示例代码使用 StringBuilder 构建字符串,最后将结果打印出来。但是连续调用了很多次 builder 对象的方法。
使用 with 函数来精简代码:
fun main() {val list = listOf("BMW", "BENZ", "AUDI", "TOYOTA", "HONDA")val result = with(StringBuilder()) {append("Car brand: \n")for (car in list) {append(car).append("\n")}append("Done.")toString()}println(result)
}
说明:给 with 函数的第一个参数传入了一个 StringBuilder 对象,那么接下来整个 Lambda 表达式的上下文就会是这个 StringBuilder 对象。于是我们在 Lambda 表达式中就不用再像刚才那样调用 builder.append() 和 builder.toString() 函数了,而是可以直接调用 append() 和 toString() 函数。Lambda 表达式的最后一行代码会作为 with 函数的返回值返回。
三、run
? run 官方文档
// 示例代码
val result = obj.run {// TODO: 这里是 obj 的上下文// run 函数的返回值"value"
}
run 函数的用法和使用场景和 with 函数非常类似,只是稍微做了一些语法改动而已。首先 run 函数不能直接调用的,而是一定要调用某个对象的 run 函数才行;其次 run 函数只接收一个 Lambda 参数,并且会在 Lambda 表达式中提供调用对象的上下文。其他方面和 with 函数是一样的,包括也会使用 Lambda 表达式中的最后一行代码作为返回值返回。
使用 run 函数来修改汽车品牌这段代码:
fun main() {val list = listOf("BMW", "BENZ", "AUDI", "TOYOTA", "HONDA")val result = StringBuilder().run {append("Car brand: \n")for (car in list) {append(car).append("\n")}append("Done.")toString()}println(result)
}
说明:只是将调用 with 函数并传入 StringBuilder 对象改成了调用 StringBuilder 对象的 run 方法。
四、apply
? apply 官方文档
// 示例代码
val result = obj.apply {// TODO: 这里是 obj 的上下文
}
apply 函数和 run 函数也是极其类似的,都要在某个对象上调用,并且只接收一个 Lambda 参数,也会在 Lambda 表达式中提供调用对象的上下文,但是 apply 函数无法指定返回值,而是会自动返回调用对象本身,
使用 apply 函数修改汽车品牌这段代码:
fun main() {val list = listOf("BMW", "BENZ", "AUDI", "TOYOTA", "HONDA")val result = StringBuilder().apply {append("Car brand: \n")for (car in list) {append(car).append("\n")}append("Done.")}println(result.toString())
}
说明:由于 apply 函数无法指定返回值,只能返回调用对象本身,因此这里的 result 实际上是一个 StringBuilder 对象,最后打印的时候再调用它的 toString() 函数才行。
五、also
? also 官方文档
// 示例代码
obj.also {// TODO: 这里是 obj 的上下文
}
also 函数的结构实际上和 let 很像,唯一的区别就是返回值的不一样,let 是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个 Unit 类型的默认值;而 also 函数返回的则是传入对象的本身。
使用 also 函数修改学习这段代码:
fun doStudy(study: Study?) {study?.also {it.readBooks()it.doHomework()}
}
also 函数使用前后和 let 函数类似。
适用场景:
| let | 适用于处理不为 null 的操作场景 |
| with | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于 Android 的 RecyclerView 的 onBinderViewHolder 中,数据 model 的属性映射到 UI 上 |
| run | 适用于 let、with 函数任何场景 |
| apply | 1. 适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象 2. 动态inflate出一个XML的View的时候需要给View绑定数据也会用到 3. 一般可用于多个扩展函数链式调用 4. 数据model多层级包裹判空处理的问题 |
| also | 适用于 let 函数的任何场景,一般可用于多个扩展函数链式调用 |
如果本文对你有帮助,请点赞支持!!!