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

fb0122發表於2019-04-08

在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"
}
複製程式碼

相關文章