Kotlin中的also、let、run、with、apply函数的用法

4,647 阅读3分钟

在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"
}