Kotlin的5个主要作用域函数

发布于:2025-06-20 ⋅ 阅读:(15) ⋅ 点赞:(0)

 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

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

特点:

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

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

class Builder (private val context: Context) {
    private var title: String = ""

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

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

上面的代码等效于:

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

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

fun add(a: Int, b: Int): Int {
    return a + b
}

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

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

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 的表达式

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

2.also

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

特点​:

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

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

 

3.let

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

特点​:

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

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

4.run

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

特点​:

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

5.with

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

特点​:

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

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

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

  • 避免 lambda 对象创建​:如果不内联,每次调用都会生成一个匿名类实例
  • 支持 return 控制流​:内联后 lambda 中的 return 可以直接从外层函数返回

 


网站公告

今日签到

点亮在社区的每一天
去签到