?applay, also,let, run, with 是kotlin標準庫提供的5個主要的作用域函數(Scope Functions)?,它們的設計目的是為了在特定作用域內更簡潔地操作對象。
如何使用這5個函數,要從它的設計目的來區分:
- apply : 配置/對象初始化, 返回對象本身
- also :?副作用操作/鏈式中間處理,返回對象本身
- let : 空安全轉換/數據映射,返回Lambda結果
- run : 對象計算/鏈式操作,返回Lambda結果
- with : 非擴展函數版的?
run
?,返回Lambda結果
深度解析每個函數
1.apply
public inline fun <T> T.apply(block: T.() -> Unit): T {block() // 配置對象return this // 返回自身
}
特點:
- 隱式?
this
?引用 - 專為對象初始化/配置設計
- ?Lambda 是擴展函數?:可以直接訪問對象成員
val dialog = AlertDialog.Builder(context).apply {setTitle("提示")setCancelable(false)}
這是一個非常典型的建造者模式。其內部的setTitle方法可能是這樣的:
class Builder (private val context: Context) {private var title: String = ""fun setTitle(title: String) = apply {this.title = title}
}
上面的代碼你可能看著有些奇怪 = apply {}
上面的代碼等效于:
fun setTitle(title: String): Builder {this.title = titlereturn this
}
這里會引入一個概念單表達式函數(Single-Expression Functions)
fun add(a: Int, b: Int): Int {return a + b
}// 標準形式:用 = 替代 { return ... }
fun add(a: Int, b: Int): Int = a + b
在看上面 無論?block
?里有多少行代碼,apply{}
??本身是一個返回?this
?的表達式?, 如果你還不理解
fun max(a: Int, b: Int){if (a > b)return aelse return b
}fun max(a: Int, b: Int): Int = if (a > b) a else b
它只是需要一個表達式,而apply{}恰好滿足這個表達式?
無論?block
?里有多少行代碼,apply
??本身是一個返回?this
?的表達式?
val person = Person().apply {name = "John" // this.name = "John"age = 30 // this.age = 30city = "New York" // this.city = "New York"
}
這個是一個初始化的例子等價與
val person = Person()
person.name = "John"
person.age = 30
person.city = "New York"
2.also
public inline fun <T> T.also(block: (T) -> Unit): T {block(this) // 執行副作用return this // 返回自身
}
特點?:
- 顯式?
it
?引用, 讓副作用操作更明確 - 可空對象處理
- 適合調試日志或鏈式調用中的中間操作?
- ?Lambda 是普通函數?:必須通過?
it
?引用對象
phone?.also {require(it)//副作用
}?.process()
?驗證并繼續使用原對象。判空如果不做類型轉換建議使用also。
?
3.let
public inline fun <T, R> T.let(block: (T) -> R): R {return block(this) // 將 this 作為參數傳入 lambda
}
特點?:
- 用?
it
?引用對象 - 適合可空對象處理和類型轉換?
val length = phone?.let { it.length } ?: 0
它的行為是把一個String類型轉換成了一個Int類型,類型轉換是它的重點。
4.run
public inline fun <T, R> T.run(block: T.() -> R): R {return block() // 以擴展函數方式調用
}
特點?:
- 用?
this
?引用對象 - 可與?
?.
?結合處理可空對象 - 適合同時訪問對象屬性和返回計算結果?
val description = user.run { "$name: ${calculateScore()}" }
5.with
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {return receiver.block() // 非擴展函數版本
}
特點?:
- 非擴展函數,需要顯式傳入接收者對象
- 不能直接結合?
?.
?處理可空對象?(需要額外判空) - 適合集中操作一個對象的場景
val result = with(config) {validate()buildResult()
}
我們可以看到這5個都是內聯函數(inline functions),其核心機制就是在編譯時進行代碼拷貝(或稱"代碼展開"),而不是在運行時進行函數調用。
為什么 Kotlin 標準庫函數用 inline?
- ?避免 lambda 對象創建?:如果不內聯,每次調用都會生成一個匿名類實例
- ?支持?
return
?控制流?:內聯后 lambda 中的?return
?可以直接從外層函數返回
?