5 个看起来差不多的函数,搞懂区别就能写出"地道 Kotlin"代码。
| 函数 | 块内引用对象 | 返回值 | 典型场景 |
|---|---|---|---|
| let | it |
lambda 结果 | 可空对象判空 + 转换x?.let { it.foo() } |
| run | this |
lambda 结果 | 多操作 + 算结果(扩展函数)x.run { foo(); bar() } |
| with | this |
lambda 结果 | 多操作 + 算结果(普通函数)with(x) { foo(); bar() } |
| apply | this |
对象本身 | 配置对象(设字段)X().apply { name = "..." } |
| also | it |
对象本身 | 链中加日志 / 校验 / 副作用x.also { log(it) } |
想要返回结果(计算/转换)→ let / run / with
想要返回对象本身(链式/配置)→ apply / also
块内用 it(参数式)→ let / also
块内用 this(接收者式)→ run / with / apply
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T, R> T.run(block: T.() -> R): R = block()
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
| 场景 | 选哪个 |
|---|---|
| 判空后做点事再返回结果 | ?.let { } |
| 配置一个新对象(设 N 个字段) | .apply { } |
| 链中加 println 调试 | .also { println(it) } |
| 多操作然后算 toString | .run { ... } |
| 非空对象 + 多次访问其成员 | with(x) { ... } |
| 临时变量不污染外层作用域 | .let { tmp -> ... } |
需求:"给一个 user 对象做点事,最后打印它的 name"。注意每个写法的差异。
val r = user.let {
println(it.name)
it.name.length
}
val r = user.run {
println(name)
name.length
}
val r = with(user) {
println(name)
name.length
}
val r = user.apply {
println(name)
name.length
// ↑ 这行被丢弃
}
val r = user.also {
println(it.name)
it.name.length
// ↑ 这行被丢弃
}
let / run / with:lambda 最后一行是返回值,所以你能拿到 lengthapply / also:返回的是原对象,length 被丢了this(run / with / apply)能省略 it.,但嵌套时容易混淆it(let / also)显式但安全,链式特别清楚同一个需求:构建一个 HTTP 请求,配置参数,发送,处理响应,打印结果。
val request = HttpRequest()
request.url = "https://api.example.com/users"
request.method = "POST"
request.body = """{"name":"Alice"}"""
request.headers["Content-Type"] = "application/json"
println("准备: $request")
val response = request.execute()
val parsed = parseJson(response)
println("解析: $parsed")
val request = HttpRequest().apply {
url = "https://api.example.com/users"
method = "POST"
body = """{"name":"Alice"}"""
headers["Content-Type"] = "application/json"
}
println("准备: $request")
val response = request.execute()
val parsed = parseJson(response)
println("解析: $parsed")
val parsed = HttpRequest().apply {
url = "https://api.example.com/users"
method = "POST"
body = """{"name":"Alice"}"""
headers["Content-Type"] = "application/json"
}.also { println("准备: $it") }
.execute()
.run { parseJson(this) }
.also { println("解析: $it") }
data class HttpRequest(
val url: String,
val method: String = "GET",
val body: String? = null,
val headers: Map<String, String> = emptyMap(),
)
val parsed = HttpRequest(
url = "https://api.example.com/users",
method = "POST",
body = """{"name":"Alice"}""",
headers = mapOf("Content-Type" to "application/json"),
).also { println("准备: $it") }
.execute()
.let(::parseJson)
.also { println("解析: $it") }
下面是一个简化的"输入 → 转换 → 输出"实验台。