🌟 前言
Kotlin 里有五个"看似相似、实则各有门派"的函数:
let、with、run、apply、also。
它们是 Kotlin 官方定义的"标准函数(Standard Functions)",
被誉为:
✨"让代码更简洁的五大法术"。
可是在面试中,它们也是让人最头疼的考点之一:
💬"run 和 apply 有什么区别?"
💬"什么时候用
let比also更合适?"💬"
with为什么几乎不推荐用了?"
今天,我们不光要讲清楚每个函数的 定义 + 场景 + 区别,
还要让你最后能用一句话轻松记住它们!
🧩 一、let ------ 最常用的空安全与链式利器
🔹 定义
kotlin
inline fun <T, R> T.let(block: (T) -> R): R
👉 把当前对象(this)作为参数 it 传入 block 中执行,返回 block 的结果。
🔹 使用场景 1:空安全
bash
user?.let {
println("User name: ${it.name}")
}
只有在 user 非空时才执行,防止空指针异常(NullPointerException)。
🔹 使用场景 2:链式操作
scss
val result = text?.let { it.trim() }?.let { it.uppercase() }
可连续调用,简洁优雅。
🔹 使用场景 3:临时作用域
bash
val length = "Kotlin".let {
println("String is: $it")
it.length
}
减少全局变量污染。
✅ 一句话理解: let 用于安全调用或链式处理,返回最后一行表达式的结果。
⚙️ 二、with ------ 对同一对象执行多操作的老派绅士
🔹 定义
kotlin
inline fun <T, R> with(receiver: T, block: T.() -> R): R
with 是一个普通函数,不是扩展函数。
需要把对象作为参数传入。
🔹 使用场景:连续操作一个对象
ini
with(paint) {
color = Color.RED
style = Paint.Style.FILL
strokeWidth = 3f
}
🔹 DSL 风格构建
scss
val text = with(StringBuilder()) {
append("Hello, ")
append("Kotlin!")
toString()
}
✅ 一句话理解: with 适合对同一对象进行多步操作,并返回结果。
⚡ 三、run ------ 既能执行又能返回的多面手
🔹 定义
kotlin
inline fun <T, R> T.run(block: T.() -> R): R
run 的语义是"执行一段代码,并返回结果"。
常用于"初始化 + 返回值"。
🔹 使用场景 1:对象初始化 + 返回结果
scss
val config = Config().run {
setMode("Dark")
setSize(1080, 1920)
this // 返回整个对象
}
🔹 使用场景 2:空安全执行逻辑
arduino
val len = text?.run {
println("Length: $length")
length
} ?: 0
✅ 一句话理解: run 在对象上下文中执行逻辑,并返回表达式结果。
🔮 四、apply ------ 对象初始化的终极神器
🔹 定义
kotlin
inline fun <T> T.apply(block: T.() -> Unit): T
apply 与 run 的区别是:返回对象本身。
🔹 使用场景:创建并配置对象
scss
val dialog = AlertDialog.Builder(context).apply {
setTitle("Warning")
setMessage("Are you sure?")
setPositiveButton("Yes", null)
}.create()
🔹 连贯初始化
scss
val json = JSONObject().apply {
put("name", "Kotlin")
put("version", "1.9")
}.toString()
✅ 一句话理解: apply 用于初始化对象,返回对象自身。
💡 五、also ------ 调试与副作用专用的隐形帮手
🔹 定义
kotlin
inline fun <T> T.also(block: (T) -> Unit): T
与 apply 类似,但 also 使用 it 而不是 this。
🔹 使用场景 1:打印调试
kotlin
val user = User("Tom", 18).also {
println("Created user: $it")
}
🔹 使用场景 2:链式调用插入副作用
scss
val list = mutableListOf(1, 2, 3)
.also { println("Before add: $it") }
.apply { add(4) }
.also { println("After add: $it") }
✅ 一句话理解: also 用于链式副作用(打印、调试、记录),返回对象自身。
🧠 六、五大函数对比总结表
| 函数 | 是否扩展函数 | 作用域对象 | Lambda 参数 | 返回值 | 常用场景 |
|---|---|---|---|---|---|
let |
✅ | it |
参数 | 最后一行结果 | 空安全、链式调用 |
with |
❌ | this |
无 | 最后一行结果 | 对象多属性操作 |
run |
✅ | this |
无 | 最后一行结果 | 执行逻辑 + 返回值 |
apply |
✅ | this |
无 | 对象自身 | 初始化配置对象 |
also |
✅ | it |
参数 | 对象自身 | 副作用(日志、调试) |
💬 七、面试高频问题
Q1:apply 和 run 有什么区别?
apply 返回对象本身,常用于初始化;run 返回表达式结果,常用于执行逻辑。
Q2:let 和 also 都有 it,怎么区分?
let 用于处理结果或转换数据;also 用于插入副作用(打印、记录),不改变结果。
Q3:with 现在还推荐用吗?
Kotlin 官方推荐用 run 或 apply 代替,
因为
with不是扩展函数,链式调用不方便。
🚀 八、实战对比:优雅的 Kotlin 写法
原始写法:
ini
if (user != null) {
user.name = "Tom"
user.save()
}
Kotlin 写法:
ini
user?.apply {
name = "Tom"
save()
}
更安全、更简洁、更 Kotlin。
🧩 九、一句话记住它们!
🪄 口诀:
let看结果,
with管上下文,
run执逻辑要返回,
apply初始化最方便,
also插副作用不改变。
🧠 总结
Kotlin 的这五个函数并不是炫技的糖衣,
它们是让你的代码表达更自然、结构更安全的核心。
理解它们的语义 + 返回值 + 使用场景,
你不仅能写出更优雅的 Kotlin,还能在面试中轻松应对所有关于它们的问题。
一句话总结:
五大函数不同在「返回值」与「使用场景」,
记住口诀,写出 Kotlin 的优雅与效率! ✅