前置知识: Kotlin

Kotlin作用域函数

00:00
3 min Beginner 2026/6/14

let、run、with、apply、also

概述

Kotlin 提供了五个作用域函数:let、run、with、apply、also。它们的核心功能是在一个对象上执行一段代码块,但各自在引用对象的方式和返回值上有所不同。选择合适的作用域函数可以让代码更简洁、更易读。

很多初学者觉得这五个函数容易混淆,但只要理解两个维度的区别就能轻松选择:一是如何引用对象(it 还是 this),二是返回什么(对象本身还是 lambda 的结果)。

基础概念

五个作用域函数的区别可以归纳为两个维度:

  • 引用方式it(let、also)还是 this(run、with、apply)
  • 返回值:返回对象本身(apply、also)还是 lambda 结果(let、run、with)
函数引用方式返回值是否是扩展函数
letitlambda 结果
runthislambda 结果
withthislambda 结果否(参数
applythis对象本身
alsoit对象本身

快速上手

data class Person(var name: String, var age: Int)

fun main() {
    val person = Person("Alice", 25)

    // let:用 it 引用,返回 lambda 结果
    val greeting = person.let { "${it.name} is ${it.age}" }
    println(greeting)  // Alice is 25

    // run:用 this 引用,返回 lambda 结果
    val description = person.run { "$name is $age years old" }
    println(description)  // Alice is 25 years old

    // with:不是扩展函数,用 this 引用
    val info = with(person) { "$name, age $age" }
    println(info)  // Alice, age 25

    // apply:用 this 引用,返回对象本身
    val configured = person.apply {
        name = "Bob"
        age = 30
    }
    println(configured)  // Person(name=Bob, age=30)

    // also:用 it 引用,返回对象本身
    val logged = person.also {
        println("设置完成: $it")
    }
    println(logged)  // Person(name=Bob, age=30)
}

详细用法

let — 空安全检查与转换

let 最常见的用途是处理空类型

// 不使用 let:需要先判空
fun processName(name: String?) {
    if (name != null) {
        println(name.uppercase())
        println(name.length)
    }
}

// 使用 let:更简洁
fun processNameWithLet(name: String?) {
    name?.let {
        println(it.uppercase())
        println(it.length)
        // 在这个 lambda 内,it 是非空的 String
    }
}

// let 用于转换
val length = "Hello".let { it.length }
println(length)  // 5

// let 用于链式调用
val result = "  Hello World  "
    .let { it.trim() }
    .let { it.lowercase() }
    .let { it.replace(" ", "_") }
println(result)  // hello_world

run — 执行计算并返回结果

run 适合在对象执行多个操作返回一个结果

// 在对象上计算
val person = Person("Alice", 25)
val isAdult = person.run {
    // this 指向 person,可以省略 this
    age >= 18
}
println(isAdult)  // true

// run 也可以不作为扩展函数使用
val result = run {
    // 执行一段代码,返回最后一个表达式
    val x = 10
    val y = 20
    x + y
}
println(result)  // 30

// run 用于初始化和计算
val config = run {
    val env = System.getenv("ENV") ?: "development"
    val port = System.getenv("PORT")?.toInt() ?: 8080
    Config(env, port)
}

with — 对对象执行多个操作

with 不是扩展函数,适合一个对象多个操作而不需要返回对象本身:

val person = Person("Alice", 25)

// 使用 with 对对象执行多个操作
val description = with(person) {
    // this 指向 person
    println("处理 $name 的数据")
    name.uppercase()
    // 返回最后一个表达式的值
}
println(description)  // ALICE

// with 常用于构建字符串或其他对象
val output = with(StringBuilder()) {
    append("姓名: ")
    append(person.name)
    append("\n年龄: ")
    append(person.age)
    toString()
}
println(output)

apply — 配置对象

apply 返回对象本身,适合用于初始化配置对象:

// 配置对象
val person = Person("Alice", 25).apply {
    // this 指向 person,可以省略 this
    name = "Bob"
    age = 30
}
println(person)  // Person(name=Bob, age=30)

// 配置复杂对象
val request = HttpRequest.Builder().apply {
    url = "https://api.example.com"
    method = "POST"
    headers["Content-Type"] = "application/json"
    body = """{"name":"Alice"}"""
    timeout = 5000
}.build()

// apply 在 Android 中特别常用
val textView = TextView(context).apply {
    text = "Hello"
    textSize = 16f
    setTextColor(Color.BLACK)
}

also — 附加操作

also 返回对象本身,适合在链式调用插入额外操作(如日志验证):

// also 用于日志
val numbers = mutableListOf(1, 2, 3)
    .also { println("原始列表: $it") }
    .apply { add(4) }
    .also { println("添加后: $it") }
    .apply { removeAt(0) }
    .also { println("删除后: $it") }

// also 用于验证
val user = User("Alice", 25)
    .also {
        require(it.name.isNotEmpty()) { "名字不能为空" }
        require(it.age >= 0) { "年龄不能为负数" }
    }

// also 用于调试
val result = computeValue()
    .also { println("计算结果: $it") }
    .let { it * 2 }
    .also { println("翻倍后: $it") }

常见场景

链式处理

// 读取输入、处理、输出
fun processInput(input: String?): String {
    return input
        ?.let { it.trim() }                    // 去除空白
        ?.takeIf { it.isNotEmpty() }           // 检查非空
        ?.let { it.uppercase() }               // 转大写
        ?: "DEFAULT"                            // 默认值
}

构建对象

// 使用 apply 构建配置对象
data class ServerConfig(
    var host: String = "localhost",
    var port: Int = 8080,
    var debug: Boolean = false,
    var maxConnections: Int = 100
)

fun createConfig(): ServerConfig = ServerConfig().apply {
    host = System.getenv("HOST") ?: "0.0.0.0"
    port = System.getenv("PORT")?.toInt() ?: 9090
    debug = System.getenv("DEBUG") == "true"
    maxConnections = 200
}

空安全链式调用

// 安全地获取嵌套属性
class Company(val address: Address?)
class Address(val city: String?)

val company: Company? = getCompany()

// 不使用 let:多层判空
val city1: String? = if (company != null && company.address != null) {
    company.address.city
} else null

// 使用 let:更简洁
val city2: String? = company?.let { it.address?.city }

注意事项

  • 不要过使用:如果只是简单调用一个方法,不需要作用域函数
  • 避免嵌套嵌套使用作用域函数会让代码难以阅读,特别是 it 的指向容易混淆
  • 选择 it 还是 this:如果 lambda 内需要频繁访问对象属性,用 this(run/apply/with)更简洁;如果只是偶尔使用,用 it(let/also)更明确
  • also 和 apply 的区别:also 用 it 引用,适合附加操作;apply 用 this 引用,适合配置对象
  • let 不要滥用:let 主要用于空安全检查,不要为了”函数式”而把所有代码都放进 let

进阶用法

自定义作用域函数

// 类似 let 但可以提供默认值
inline fun <T : Any, R> T?.letOrDefault(default: R, block: (T) -> R): R {
    return if (this != null) block(this) else default
}

// 使用
val name: String? = null
val length = name.letOrDefault(0) { it.length }
println(length)  // 0

// 类似 apply 但可以条件执行
inline fun <T> T.applyIf(condition: Boolean, block: T.() -> Unit): T {
    if (condition) block()
    return this
}

// 使用
val person = Person("Alice", 25).applyIf(age > 18) {
    name = name.uppercase()
}

作用域函数与协程

import kotlinx.coroutines.*

fun main() = runBlocking {
    // 在协程中使用作用域函数
    val result = withContext(Dispatchers.IO) {
        // 模拟网络请求
        delay(100)
        "Hello"
    }.let { response ->
        // 处理响应
        response.uppercase()
    }.also {
        // 记录日志
        println("处理结果: $it")
    }
    println(result)  // HELLO
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式