Kotlin Scope Functions (作用域函式)

Kotlin 標準函式庫提供了 5 個非常有名的函式:let, run, with, apply, also。 它們的功能非常相似:在一個物件的 Context (作用域) 中執行程式碼。 差別在於:

  1. 這個物件在 block 裡面是用 this 還是 it 參考?
  2. 這個函式最後回傳的是 物件本身 還是 Block 的執行結果

這張表背起來就對了:

函式物件參考回傳值主要用途
letitBlock 結果配合 ?.let 做 Null check
applythis物件本身物件初始化與設定
runthisBlock 結果執行區塊並計算結果
alsoit物件本身額外操作 (如 Log)
withthisBlock 結果針對一個物件多次操作 (非 Extension)

let

最常用來處理 Nullable 物件。

val name: String? = "Miko"
name?.let { 
    // 只有當 name 不是 null 時才會執行這裡
    println("Name length is ${it.length}")
}

apply

最常用來設定物件初始化屬性(尤其是 Android 的 Intent 或 View)。

val person = Person().apply {
    name = "Miko"
    age = 18
} // 回傳設定好的 person 物件

also

適合用來做「副作用」操作,例如 Log,不影響原本流程。

val person = Person("Miko").also {
    println("Create person: $it")
}

run

run 有兩種用法:

  1. Scope Function: 類似 apply,但是回傳的是 Block 的結果 (最後一行),而不是物件本身。
  2. Standard Function: 用來執行一個區塊並計算結果。
// 1. 作為 Scope Function (T.run)
val result = "Hello World".run {
    // 這裡用 this
    println("Original length: $length") 
    length + 10 // 回傳這個計算結果
}

// 2. 作為標準函式 (run { })
val calculatedValue = run {
    val x = 10
    val y = 20
    x + y
}

with

with 不是 Extension Function,它是一個獨立的函式。 用法:with(object) { ... }。 適合用來對同一個物件進行連續操作,省去重複寫變數名稱。

val webView = WebView(context)
with(webView) {
    settings.javaScriptEnabled = true
    loadUrl("https://www.google.com")
}

該怎麼選?

這看起來很亂,但其實有簡單的選擇邏輯:

你的目的是什麼?我該選誰?
我要做 Null Check (?.)let
我要初始化/設定物件apply
我要做額外操作 (Log/Print)also
我要執行運算並回傳結果run
我要對一個物件連續呼叫方法with