Kotlin?作用域函數(shù)apply、let、run、with、also使用指南
在 Kotlin 開發(fā)中,作用域函數(shù)(Scope Functions)是一組能讓代碼更簡潔、更函數(shù)式的高階函數(shù)。它們通過不同的作用域規(guī)則和返回值設(shè)計(jì),解決了對(duì)象配置、空安全處理、鏈?zhǔn)讲僮鞯瘸R妶?chǎng)景問題。本文將結(jié)合核心特性、代碼示例和對(duì)比表格,助你精準(zhǔn)掌握 apply、let、run、with、also 的使用精髓。
一、引言:為什么需要作用域函數(shù)?
在面向?qū)ο缶幊讨?,我們常需?duì)對(duì)象進(jìn)行配置(如設(shè)置屬性)、處理 null 值、執(zhí)行鏈?zhǔn)讲僮骰蚝喕蓡T訪問。傳統(tǒng)方式可能導(dǎo)致代碼冗余,而 Kotlin 的作用域函數(shù)通過作用域限定和返回值優(yōu)化,讓這些操作更優(yōu)雅。例如:
// 傳統(tǒng)方式:臨時(shí)變量 + 多次調(diào)用
val file = File("data.txt")
file.createNewFile()
file.setReadable(true)
file.setWritable(true)
// 使用 apply 簡化:
val file = File("data.txt").apply {
createNewFile()
setReadable(true)
setWritable(true)
}接下來,我們逐一解析每個(gè)函數(shù)的核心機(jī)制與適用場(chǎng)景。
二、作用域函數(shù)詳解
1. apply:對(duì)象配置的 “流式構(gòu)建器”
- 接收者引用:
this(可省略,直接調(diào)用接收者成員) - 返回值:接收者對(duì)象本身(
T) - 核心用途:對(duì)對(duì)象進(jìn)行初始化或配置,返回配置后的對(duì)象
- null 安全:不支持(接收者需非 null)
- 作用域:接收者作用域(
this指向接收者)
代碼示例:
// 配置網(wǎng)絡(luò)請(qǐng)求參數(shù)
val request = Request().apply {
url = "https://api.example.com"
method = "GET"
headers["Content-Type"] = "application/json"
}
// 簡化自定義 View 初始化
MyButton().apply {
text = "提交"
setOnClickListener { handleClick() }
setBackgroundColor(Color.BLUE)
}最佳實(shí)踐:
- 對(duì)象構(gòu)建:替代 Java 的構(gòu)建器模式,如
AlertDialog.Builder(context).apply { ... } - 避免臨時(shí)變量:直接返回配置后的對(duì)象,鏈?zhǔn)秸{(diào)用更流暢
2. let:空安全與作用域限定
- 接收者引用:
it(隱式參數(shù),作為 lambda 的唯一參數(shù)) - 返回值:lambda 的執(zhí)行結(jié)果(任意類型)
- 核心用途:處理 null 值,限定變量作用域,返回新計(jì)算結(jié)果
- null 安全:支持(配合安全調(diào)用符
?.) - 作用域:獨(dú)立作用域(
it僅在 lambda 內(nèi)可見)
代碼示例:
// 安全處理 nullable 對(duì)象
val userName: String? = "Alice"
val greeting = userName?.let { "Hello, $it!" } ?: "Hello, Guest!"
// 限定作用域,避免變量污染
val text = "Kotlin is great"
text.let {
val words = it.split(" ")
"單詞數(shù):${words.size}" // it 僅在此處有效
}最佳實(shí)踐:
- null 校驗(yàn):
obj?.let { ... }替代繁瑣的if (obj != null) - 臨時(shí)變量:在 lambda 內(nèi)創(chuàng)建臨時(shí)變量(如
val data = it.process())
3. run:接收者作用域的 “全能選手”
- 接收者引用:
this(可省略,直接調(diào)用接收者成員) - 返回值:lambda 的執(zhí)行結(jié)果(任意類型)
- 核心用途:在接收者作用域內(nèi)執(zhí)行代碼塊,混合調(diào)用成員方法和外部函數(shù)
- null 安全:不支持(需手動(dòng)校驗(yàn)接收者 null)
- 作用域:接收者作用域(優(yōu)先訪問接收者成員)
代碼示例:
// 計(jì)算文件內(nèi)容長度
val file = File("data.txt")
val contentLength = file.run {
if (exists()) readText().length else 0
}
// 鏈?zhǔn)胶瘮?shù)調(diào)用
"Android".run {
toUpperCase() // 調(diào)用接收者方法
}.run {
"$this Kotlin" // 處理中間結(jié)果
}.run(::println) // 調(diào)用外部函數(shù)(打印結(jié)果)最佳實(shí)踐:
- 成員訪問:簡化接收者成員調(diào)用(如
view.run { setText("OK") }) - 混合邏輯:同時(shí)使用接收者方法(
length)和外部函數(shù)(println)
4. with:run 的參數(shù)化變體
- 接收者引用:參數(shù)傳入(非擴(kuò)展函數(shù),直接在 lambda 中使用接收者)
- 返回值:lambda 的執(zhí)行結(jié)果(同
run) - 核心用途:以非擴(kuò)展函數(shù)形式使用
run,顯式傳入接收者 - null 安全:不支持(需手動(dòng)校驗(yàn)接收者 null)
- 作用域:接收者作用域(同
run)
代碼示例:
// 顯式傳入接收者(非擴(kuò)展函數(shù)調(diào)用)
val result = with(ArrayList<String>()) {
add("A")
add("B")
size // 返回 lambda 結(jié)果
}
// 數(shù)學(xué)計(jì)算場(chǎng)景
val point = Point(3, 4)
val distance = with(point) {
sqrt(x*x + y*y) // 直接訪問 x/y 屬性(假設(shè) Point 有 x/y 成員)
}最佳實(shí)踐:
- 多對(duì)象操作:當(dāng)接收者不是調(diào)用對(duì)象時(shí)(如
with(list, ::process)) - 替代 run:習(xí)慣參數(shù)化調(diào)用時(shí)使用(與
run功能完全一致)
5. also:鏈?zhǔn)讲僮鞯?“副作用保持者”
- 接收者引用:
it(隱式參數(shù),作為 lambda 的唯一參數(shù)) - 返回值:接收者對(duì)象本身(
T,同apply) - 核心用途:執(zhí)行副作用操作(如日志、賦值),保持對(duì)象鏈?zhǔn)秸{(diào)用
- null 安全:不支持(接收者需非 null)
- 作用域:獨(dú)立作用域(
it僅在 lambda 內(nèi)可見) 代碼示例:
// 日志記錄與鏈?zhǔn)讲僮?
val user = User().also {
it.name = "Bob" // 配置對(duì)象
println("創(chuàng)建用戶:${it.name}") // 打印日志
}
// 連續(xù)操作同一對(duì)象
File("data.txt")
.also { it.createNewFile() } // 創(chuàng)建文件
.also { it.writeText("content") } // 寫入內(nèi)容
.also { println("文件路徑:${it.path}") } // 打印路徑最佳實(shí)踐:
- 副作用處理:在鏈?zhǔn)秸{(diào)用中插入日志、賦值等非核心邏輯
- 保持對(duì)象引用:返回接收者本身,支持繼續(xù)調(diào)用其他函數(shù)(如
.also(...).apply(...))
三、對(duì)比表格:快速選擇指南
| 函數(shù) | 接收者引用 | 返回值 | 核心用途 | null 安全 | 作用域類型 | 典型場(chǎng)景 |
|---|---|---|---|---|---|---|
apply | this | 接收者對(duì)象 | 對(duì)象配置 | 否 | 接收者作用域 | 初始化對(duì)象、設(shè)置屬性 |
let | it | lambda 結(jié)果 | 空安全處理、返回新值 | 是(?.) | 獨(dú)立作用域 | 處理 nullable 對(duì)象、限定作用域 |
run | this | lambda 結(jié)果 | 成員操作 + 函數(shù)調(diào)用 | 否 | 接收者作用域 | 混合調(diào)用對(duì)象方法和外部函數(shù) |
with | 參數(shù)傳入 | lambda 結(jié)果 | 非擴(kuò)展函數(shù)形式的 run | 否 | 接收者作用域 | 顯式傳入接收者、多對(duì)象操作 |
also | it | 接收者對(duì)象 | 鏈?zhǔn)礁弊饔茫ㄈ罩?、賦值) | 否 | 獨(dú)立作用域 | 保持對(duì)象鏈?zhǔn)秸{(diào)用,執(zhí)行附加操作 |
四、最佳實(shí)踐與避坑指南
1. 對(duì)象配置首選 apply
當(dāng)需要對(duì)對(duì)象進(jìn)行初始化或設(shè)置屬性時(shí),apply 能避免臨時(shí)變量,使代碼更流暢:
// 推薦:直接返回配置后的對(duì)象
val button = Button().apply {
text = "提交"
setOnClickListener { ... }
}2. null 安全首選 let
處理可為 null 的對(duì)象時(shí),let 配合 ?. 是最佳選擇:
// 避免 NPE:安全調(diào)用 + let
networkResponse?.let { handle(it) } 3. 成員訪問首選 run/with
當(dāng)需要頻繁調(diào)用接收者成員(如 file.readText())時(shí),run 或 with 更簡潔:
// 簡化成員訪問
file.run {
if (exists()) readText() else ""
}4. 鏈?zhǔn)礁弊饔檬走x also
執(zhí)行日志記錄、變量賦值等非核心操作時(shí),also 能保持對(duì)象鏈?zhǔn)秸{(diào)用:
// 鏈?zhǔn)搅鞒讨胁迦肴罩?
downloadFile()
.also { logDownload(it) }
.also { saveToCache(it) }5. 避免混淆返回值
apply/also返回接收者對(duì)象,適合繼續(xù)配置(如.apply(...).also(...))let/run返回 lambda 結(jié)果,適合生成新值(如val result = obj.let { ... })
五、總結(jié):選擇的藝術(shù)
Kotlin 的作用域函數(shù)是函數(shù)式編程與面向?qū)ο蟮耐昝澜Y(jié)合,掌握它們的關(guān)鍵在于:
- 明確目標(biāo):配置對(duì)象用
apply,處理 null 用let,混合邏輯用run - 關(guān)注返回值:需保持對(duì)象鏈?zhǔn)秸{(diào)用選
apply/also,需計(jì)算結(jié)果選let/run - 代碼風(fēng)格:習(xí)慣擴(kuò)展函數(shù)用
apply/let/run,習(xí)慣參數(shù)化調(diào)用用with
這些函數(shù)并非互斥,而是互補(bǔ)。例如,apply 配合 also 可實(shí)現(xiàn) “配置 + 日志” 的復(fù)合操作,let 配合 run 可處理 null 值并執(zhí)行復(fù)雜邏輯。熟練運(yùn)用這組工具,能讓代碼兼具簡潔性與可讀性,真正體現(xiàn) Kotlin 的優(yōu)雅與高效。
到此這篇關(guān)于Kotlin 作用域函數(shù):apply、let、run、with、also的文章就介紹到這了,更多相關(guān)Kotlin apply let run with also內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android ViewPager循環(huán)播放廣告實(shí)例詳解
這篇文章主要介紹了Android ViewPager循環(huán)播放廣告條實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03
學(xué)習(xí)Android自定義Spinner適配器
這篇文章主要為大家詳細(xì)介紹了學(xué)習(xí)Android自定義Spinner適配器的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-05-05
Android保存的文件顯示到文件管理的最近文件和下載列表中的方法
這篇記錄的是Android中如何把我們往存儲(chǔ)中寫入的文件,如何顯示到文件管理的下載列表、最近文件列表中,需要的朋友可以參考下2020-01-01
超詳細(xì)的Android開發(fā)調(diào)試工具ADB命令及安裝介紹
這篇文章主要介紹了Android調(diào)試工具ADB安裝使用技巧,非常詳細(xì),有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家早日升值加薪2021-09-09
Android實(shí)用控件自定義逼真相機(jī)光圈View
這篇文章主要為大家詳細(xì)介紹了Android實(shí)用控件自定義逼真相機(jī)光圈,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08
android特賣列表倒計(jì)時(shí)卡頓問題的解決方法
這篇文章主要為大家詳細(xì)介紹了android特賣列表倒計(jì)時(shí)卡頓問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
Kotlin基礎(chǔ)學(xué)習(xí)之lambda中return語句詳解
這篇文章主要給大家介紹了關(guān)于Kotlin基礎(chǔ)學(xué)習(xí)之lambda中return語句的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或使用Kotlin具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
Android 超詳細(xì)SplashScreen入門教程
Android 12正式版即將發(fā)布,有一個(gè)非常顯著的視覺變化就是,Android 12強(qiáng)制給所有的App都增加了SplashScreen的功能。是的,即使你什么都不做,只要你的App安裝到了Android 12手機(jī)上,都會(huì)自動(dòng)擁有這個(gè)新功能2022-03-03
Android DataBinding單向數(shù)據(jù)綁定深入探究
看了谷歌官方文章確實(shí)寫的太簡略了,甚至看完之后有很多地方還不知道怎么回事兒或者怎么用,那么接下來我將通過文章全面介紹一下DataBinding單向數(shù)據(jù)綁定2022-11-11
HandlerThread的使用場(chǎng)景和用法詳解
這篇文章主要介紹了HandlerThread的使用場(chǎng)景和用法詳解,HandlerThread是Android中的一個(gè)線程類,它是Thread的子類,并且內(nèi)部封裝了Looper和Handler,提供了更方便的消息處理和線程操作,需要的朋友可以參考下2023-07-07

