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 可以直接从外层函数返回
相关推荐
Junerver2 小时前
如何在Jetpack Compose中轻松的进行表单验证
前端·kotlin
androidwork6 小时前
Kotlin实现文件上传进度监听:RequestBody封装详解
android·开发语言·kotlin
androidwork6 小时前
解析401 Token过期自动刷新机制:Kotlin全栈实现指南
android·kotlin
移动开发者1号12 小时前
Android使用Chucker监控网络请求
android·kotlin
移动开发者1号14 小时前
Android请求优先级调度策略杂谈
android·kotlin
alexhilton21 小时前
MVI架构:Compose中的响应式状态管理
android·kotlin·android jetpack
Junerver1 天前
Kotlin 2.1.0的新改进带来哪些改变
前端·kotlin
yzpyzp1 天前
KAPT 的版本如何升级,是跟随kotlin的版本吗
android·kotlin·gradle
泓博1 天前
KMP(Kotlin Multiplatform)简单动画
android·开发语言·kotlin