Kotlin的5个主要作用域函数

applay, also,let, run, with 是kotlin标准库提供的5个主要的作用域函数**(Scope Functions)​**,它们的设计目的是为了在特定作用域内更简洁地操作对象。

如何使用这5个函数,要从它的设计目的来区分:

  1. apply : 配置/对象初始化, 返回对象本身
  2. also : 副作用操作/链式中间处理,返回对象本身
  3. let : 空安全转换/数据映射,返回Lambda结果
  4. run : 对象计算/链式操作,返回Lambda结果
  5. with : 非扩展函数版的 run ,返回Lambda结果

深度解析每个函数

1.apply

Kotlin 复制代码
public inline fun <T> T.apply(block: T.() -> Unit): T {
    block() // 配置对象
    return this // 返回自身
}

特点:

  • 隐式 this 引用
  • 专为对象初始化/配置设计
  • Lambda 是扩展函数:可以直接访问对象成员
Kotlin 复制代码
val dialog = AlertDialog.Builder(context).apply {
            setTitle("提示")
            setCancelable(false)
        }

这是一个非常典型的建造者模式。其内部的setTitle方法可能是这样的:

Kotlin 复制代码
class Builder (private val context: Context) {
    private var title: String = ""

    fun setTitle(title: String) = apply {
        this.title = title
    }
}

上面的代码你可能看着有些奇怪 = apply {}

上面的代码等效于:

Kotlin 复制代码
fun setTitle(title: String): Builder {
    this.title = title
    return this
}

这里会引入一个概念单表达式函数(Single-Expression Functions)

Kotlin 复制代码
fun add(a: Int, b: Int): Int {
    return a + b
}

// 标准形式:用 = 替代 { return ... }
fun add(a: Int, b: Int): Int = a + b

在看上面 无论 block 里有多少行代码,apply{}本身是一个返回 this 的表达式​, 如果你还不理解

Kotlin 复制代码
fun max(a: Int, b: Int){
    if (a > b)
        return a
     else 
        return b
}

fun max(a: Int, b: Int): Int = if (a > b) a else b

它只是需要一个表达式,而apply{}恰好满足这个表达式

无论 block 里有多少行代码,apply本身是一个返回 this 的表达式

Kotlin 复制代码
val person = Person().apply {
    name = "John"      // this.name = "John"
    age = 30           // this.age = 30
    city = "New York"  // this.city = "New York"
}
这个是一个初始化的例子等价与
Kotlin 复制代码
val person = Person()
person.name = "John"
person.age = 30
person.city = "New York"

2.also

Kotlin 复制代码
public inline fun <T> T.also(block: (T) -> Unit): T {
    block(this) // 执行副作用
    return this // 返回自身
}

特点​:

  • 显式 it 引用, 让副作用操作更明确
  • 可空对象处理
  • 适合调试日志链式调用中的中间操作
  • Lambda 是普通函数 :必须通过 it 引用对象
Kotlin 复制代码
phone?.also {
 require(it)//副作用 
}?.process()

验证并继续使用原对象。判空如果不做类型转换建议使用also。

3.let

Kotlin 复制代码
public inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this) // 将 this 作为参数传入 lambda
}

特点​:

  • it 引用对象
  • 适合可空对象处理类型转换
Kotlin 复制代码
val length = phone?.let { it.length } ?: 0

它的行为是把一个String类型转换成了一个Int类型,类型转换是它的重点。

4.run

Kotlin 复制代码
public inline fun <T, R> T.run(block: T.() -> R): R {
    return block() // 以扩展函数方式调用
}

特点​:

  • this 引用对象
  • 可与 ?. 结合处理可空对象
  • 适合同时访问对象属性和返回计算结果
Kotlin 复制代码
val description = user.run { "$name: ${calculateScore()}" }

5.with

Kotlin 复制代码
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block() // 非扩展函数版本
}

特点​:

  • **非扩展函数,**需要显式传入接收者对象
  • 不能直接结合 ?. 处理可空对象(需要额外判空)
  • 适合集中操作一个对象的场景
Kotlin 复制代码
val result = with(config) {
    validate()
    buildResult()
}

我们可以看到这5个都是内联函数(inline functions),其核心机制就是在编译时进行代码拷贝(或称"代码展开"),而不是在运行时进行函数调用。

为什么 Kotlin 标准库函数用 inline?

  • 避免 lambda 对象创建:如果不内联,每次调用都会生成一个匿名类实例
  • 支持 return 控制流 :内联后 lambda 中的 return 可以直接从外层函数返回
相关推荐
hsx66612 小时前
Kotlin 协程中的 Dispatchers
kotlin
每次的天空15 小时前
Android-重学kotlin(协程源码第二阶段)新学习总结
android·学习·kotlin
stevenzqzq15 小时前
Kotlin 中主构造函数和次构造函数的区别
android·kotlin
开发者如是说18 小时前
言叶是如何对文件进行端到端加密的
android·kotlin·swift
小李飞飞砖19 小时前
kotlin
开发语言·单例模式·kotlin
小李飞飞砖19 小时前
kotlin中的冷流和热流
android·开发语言·kotlin
Kotlin上海用户组2 天前
Koin vs. Hilt——最流行的 Android DI 框架全方位对比
android·架构·kotlin
Kapaseker2 天前
当Object遇到Json你可能会碰到的坑
kotlin
RichardLai882 天前
Kotlin Flow:构建响应式流的现代 Kotlin 之道
android·前端·kotlin
程序员江同学2 天前
Kotlin/Native 编译流程浅析
android·kotlin