Kotlin 作用域函数全解析:let、run、with、apply、also 应该怎么选?
Kotlin 提供了一套优雅的“作用域函数”(Scope Functions),包括:let
、run
、with
、apply
和 also
。它们看起来相似,行为上也有交集,但却各有侧重。掌握它们的使用场景,不仅能让代码更简洁,还能让意图更加明确。
📌 什么是作用域函数?
作用域函数的作用是:在一个对象的上下文中执行一段代码块,并返回某个结果。
所有作用域函数的行为可以分成两方面:
分类 | 内容 |
---|---|
上下文对象访问方式 | 通过 this 还是 it 来引用对象 |
返回值类型 | 返回原始对象,还是返回 lambda 表达式的最后一行结果 |
🧩 总览对比表
函数 | 访问方式 | 返回值 | 是否扩展函数 | 常见用途 |
---|---|---|---|---|
let | it | lambda 结果 | ✅ | 可空判断、链式转换、局部作用域 |
run | this | lambda 结果 | ✅ | 表达式计算、初始化逻辑 |
with | this | lambda 结果 | ❌ | 对已有对象进行多次操作 |
apply | this | 对象本身 | ✅ | 初始化对象属性链式构建 |
also | it | 对象本身 | ✅ | 调试、日志、链式副作用 |
🧪 逐个详解 + 示例
1. let
:适合可空对象链式处理
let
最大的特点是使用 it
访问对象,并返回 lambda 表达式最后的值,常用于安全调用、转换或临时作用域。
val name: String? = "Kotlin"val length = name?.let {println("名字是:$it")it.length
}
常见用途:
- 安全调用链(null 安全)
- 表达式转换(如 map)
- 限定临时作用域变量名(如
it
改为具体名)
2. run
:适合表达式求值或初始化计算
run
使用 this
引用对象,返回表达式的结果。可以用于对象配置,也可以直接作为代码块返回值。
val result = "Hello".run {println("原字符串是 $this")length + 10
}
常见用途:
- 表达式求值
- 对象初始化后立即使用
3. with
:适合已有对象的多次操作
with
不是扩展函数,而是一个普通函数。它的写法比较自然,像在“带着对象做事情”。
val builder = StringBuilder()
val result = with(builder) {append("Hello, ")append("World!")toString()
}
常见用途:
- 构造复杂对象时,连续操作对象属性
- 避免重复写对象名
4. apply
:返回对象自身,用于构建对象
apply
使用 this
引用对象,返回对象本身。常用于构造函数 + 初始化链式调用。
val user = User().apply {name = "Alice"age = 20
}
常见用途:
- 初始化对象并配置属性
- DSL(领域特定语言)构建,如
View.apply {}
5. also
:副作用处理,不改变原值
also
使用 it
引用对象,返回对象本身。适合用于打印、日志、调试、不影响主逻辑的操作。
val numbers = mutableListOf("one", "two", "three").also { println("原始数据:$it") }.apply { add("four") }
常见用途:
- 日志打印
- 调试时插入查看变量值
- 链式调用中临时操作对象
🎯 使用建议:如何选择?
你可以用以下口诀快速判断:
- 🤔 我需要结果? →
run
/with
/let
- 🛠️ 我要改对象? →
apply
/with
- 🧪 我想插入副作用? →
also
- ❓ 可能为 null? →
?.let
- 📦 要链式构建对象? →
apply
🧠 小技巧:记住两个维度
返回谁?
- 返回对象本身:apply, also
- 返回表达式结果:let, run, with用谁访问对象?
- this:apply, run, with
- it:let, also