1. 讲啥的
kotlin的扩展函数很多,我这里指的基本的扩展函数,具体是
with(不是扩展函数)、let、run、apply、also这五个
2. 背景
最近写代码,被mentor评论道:有时间多看看with、let、run、apply、also这五个函数的作用特点😂。虽然自己都知道这五个函数,但具体哪个要用在什么情况上面,确实还是一知半解,现在都是also,apply一刀切。于是利用假期时间,学习一下
3. 正文
总体上来说,我将这五个函数,分为两部分。划分的依据是返回值,具体划分结果是:
with、let、run为一组,apply、also为一组。因为从默认情况来看,with、let、run都是以闭包形式,将表达式的最后一行作为返回值,而apply、also是返回对象本身。下面将依次介绍
3.1 with
特点:将对象作为参数传入,函数内使用this指代对象(this可省略),最后一行作为返回值。
缺点:不能方便地判空
先来看个例子吧
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
val person = Person()
val personName = with(person) {
name = "张三"
age = 21
gender = "男"
name // 最后一行,作为返回值
}
println(personName)
}
但假如传入的对象为空对象,就有可能出现空指针异常了。除此之外,with是一个内联函数,而非扩展函数。
3.2 let
特点:是对象的扩展函数,函数内使用it指代对象(it不可省略),最后一行作为返回值
优点:可克服with的缺点,较为方便地判空。但it不能省
看个例子
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
val person = Person()
val personName = person?.let {
it.name = "张三"
it.age = 21
it.gender = "男"
it.name // 最后一行作为返回值
}
println(personName)
}
这里即使对象是空,也能较为方便地进行兜底。不太好的点是,it不能省略
3.3 run
特点: 是对象的扩展函数,函数内使用this指代对象(this可省略),最后一行作为返回值
优点: 结合了with和let的优点
看个例子
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
val person = Person()
val personName = person?.run {
name = "张三"
age = 21
gender = "男"
name // 最后一行作为返回值
}
println(personName)
}
可以看到,既可以较为方便地判空,又可以省略this
3.4 apply
特点: 是对象的扩展函数,函数内使用this指代对象(this可省略),返回对象本身
看个例子
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
var person = Person()
person = person?.apply {
name = "张三"
age = 21
gender = "男"
}
println(person.toString())
}
我个人把它理解为run函数的特殊情况,因为两者都是使用this指代对象,this都可以省略,而且run也可以返回对象本身,比如上面的代码,也可以这样写
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
var person = Person()
person = person?.run {
name = "张三"
age = 21
gender = "男"
this // 最后一行返回对象本身
}
println(person.toString())
}
3.5 also
特点:是对象的扩展函数,函数内使用it指代对象(it不可省略),返回对象本身
看个例子
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
var person = Person()
person = person?.also {
it.name = "张三"
it.age = 21
it.gender = "男"
}
println(person.toString())
}
我个人把它理解为let函数的特殊情况,因为两者都是使用it指代对象,it都不可省略,而且let也可以返回对象本身,比如上面的代码,也可以这样写
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
var person = Person()
person = person?.let {
it.name = "张三"
it.age = 21
it.gender = "男"
it
}
println(person.toString())
}
4. 总结
从主观上来总结,apply和also用的最多,run和let用的次之,with用的非常非常少。
至于apply和also的区分:当需要给对象内的属性赋值的时候,用apply更合理,当需要利用对象的属性的时候,用also更合理。run和let也是一样的道理。看个例子
class Person {
var name: String ?= ""
var age: Int ?= 0
var gender: String ?= ""
}
fun main() {
var person = Person()
person = person?.apply {
name = "张三"
age = 21
gender = "男"
}
println(person.toString())
}
这种就是对name、age、gender这些属性赋值,用apply更合适。而假如要用到这些属性的话,比如
fun main() {
var person = Person()
person = person?.also {
println(it.name)
doSomething(it.age, it.gender)
}
}
那就是also更合适了。这些都是基于本人的小经验总结的。如有错误之处,还请各位批评指正
最后,来个表总结下
| 函数名 | 返回值 | 函数内指代对象 |
|---|---|---|
| with | 函数最后一行 | this(可省略) |
| let | 函数最后一行 | it(不可省略) |
| run | 函数最后一行 | this(可省略) |
| apply | 对象本身 | this(可省略) |
| also | 对象本身 | it(不可省略) |
其中,除with外,其余4个均为扩展函数。