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 可以直接从外层函数返回
相关推荐
FunnySaltyFish7 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker13 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z3 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton4 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream4 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam4 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker5 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
糖猫猫cc5 天前
Kite:两种方式实现动态表名
java·kotlin·orm·kite
如此风景5 天前
kotlin协程学习小计
android·kotlin