在Kotlin中有几个十分相似的标准库函数,他们之间也有一些差异,如果使用不当可能回得到与预期相反的效果,所以我们来简短的区分一下let、also、run、with、apply 这5个标准库函数的区别。 Kotlin提供了这几种标准域函数主要是为了简化一些操作,让代码看起来更加的简洁,可读性更好。
在最开始我们先创建一个简单的Book类,作为例子我们来看看每种函数的不同
data class Book(var name: String, var author: String, var price: String){
fun adjust( value: Int){
price = price.plus(value)
}
}
fun main(){
}
复制代码
上述是一个非常简单的Book类,包括三个属性:书名、作者、价格。然后有一个调整价格的方法。
- let
Book("《海边的卡夫卡》", "村上春树", 59).let {
println(it)
it.adjust(-5) //let的参数为it,也就是自身
println(it)
}
复制代码
在Book类的main函数中添加上述代码,并且运行,结果如下:
Book(name=《海边的卡夫卡》, author=村上春树, price=59) Book(name=《海边的卡夫卡》, author=村上春树, price=54)
在上述代码中,我们可以看到let的参数为自身,即:block: .(T)
,将自身作为参数传递。
- run
如果我们要用run函数实现与let一样的功能,应该是这样的:
Book("《海边的卡夫卡》", "村上春树", 59).run {
println(this)
this.adjust(-5)
println(this)
}
复制代码
可以看出来,run更像是Book对象的扩展函数,即:block: T.()
。他是将this作为参数传递,在大多数情况下this可以被省略,因此我们可以更加关注内部实现。
- with
同样的,如果要实现上述目的,我们使用with应该是这样的:
with(Book("《海边的卡夫卡》", "村上春树", 59)){
println(this)
this.adjust(-5)
println(this)
}
复制代码
这里要说明的一点是,with与其余4个库函数最大的不同就在于with不是一个扩展函数。with是一个普通函数,那么如果我们在with中传递了一个可为空的参数时,with函数将会变成:
val book: Book? = Book("《海边的卡夫卡》", "村上春树", 59)
with(book){
println(this)
this!!.adjust(-5) //将空判断放在了with方法内部
println(this)
}
复制代码
可以看到我们是将空判断放在了with内部,显然这样不是一个非常好的实现,如果我们使用run,就可以很方便的解决这个问题:
val book: Book? = Book("《海边的卡夫卡》", "村上春树", 59)
book?.run{
println(this)
this.adjust(-5)
println(this)
}
复制代码
- also
上面我们说到了,let与run的区别就在于传递的参数不同,那么let与also的区别就在于返回值的不同,他们的参数都是it,但是let返回的是lamda表达式的结果。而also返回的是上下文,即:this。 我们看一下下面的代码。
Book("《海边的卡夫卡》", "村上春树", 59)
.let {
println(it)
it.adjust(-5)
it //因为adjust()方法没有返回值,我们需要将调整价格后的Book对象作为lamda表达式的返回值返回
}
.let {
print(it)
}
Book("《海边的卡夫卡》", "村上春树", 59)
.also {
println(it)
it.adjust(-5) // 由于also直接返回当前对象,所以我们不用再提供返回值
}
.let {
print(it)
}
复制代码
上述两段代码都输出:
Book(name=《海边的卡夫卡》, author=村上春树, price=59) Book(name=《海边的卡夫卡》, author=村上春树, price=54)
如果我们将第一段代码中的第一个let{}中最后一个it返回值去掉,则会输出:
Book(name=《海边的卡夫卡》, author=村上春树, price=59)
Kotlin Unit
可以看到,let输出的是lamda表达式的值,而also的返回值是this. 通过also与let的配合,我们可以写出一些可读性更强的链式调用的语句。
- apply
对于apply函数来说,传递的参数是this, 返回值也是this。当然apply还有另一个作用,就是可以轻松的实现Builder模式(这里我们使用另一个对象Persion):
Persion().apply{
name = "小明"
age = "13"
brithday = "2000-01-01"
}
复制代码