Kotlin 中作用域函数 let、with、run、also、apply 的核心使用指南

以下是 Kotlin 中作用域函数 letwithrunalsoapply 的核心使用指南,结合其特性、适用场景及代码示例整理而成:


🧩 ​一、作用域函数核心对比

函数 上下文对象引用 返回值 典型场景
​**let**​ it Lambda 结果 空安全操作、数据转换、链式处理中间结果
​**run**​ this Lambda 结果 对象配置与计算混合、替代 with(扩展函数形式)、链式空安全处理
​**with**​ this Lambda 结果 集中操作非空对象的多属性/方法(需显式传入对象)
​**apply**​ this 对象本身 对象初始化配置(类似 Builder 模式)、多属性设置
​**also**​ it 对象本身 附加操作(如日志、验证)、链式调用中插入副作用

💡 ​选择关键​:

  • 需返回对象本身 → apply/also;需返回计算结果 → let/run/with
  • 上下文引用偏好 → thisrun/with/apply)更简洁;itlet/also)避免命名冲突。
  • 空安全 → ?.let?.run 优先。

⚙️ ​二、分函数详解与示例

1. ​**let:空安全转换与作用域隔离**​

  • 场景​:处理可空对象、数据转换、链式操作中间处理。

  • 示例​:

    kotlin 复制代码
    val name: String? = "Kotlin"
    val length = name?.let { 
        println("Processing: $it")  // 输出:Processing: Kotlin
        it.length                   // 返回结果
    } ?: 0                         // 提供默认值

    典型链式转换​:

    ini 复制代码
    val numbers = listOf(1, 2, 3)
    val doubled = numbers
        .filter { it > 1 }
        .let { it.map { num -> num * 2 } }  // [4, 6]

2. ​**run:对象配置与计算混合**​

  • 场景 ​:对象初始化+结果计算、替代 with(扩展函数形式)、临时作用域创建。

  • 示例​:

    kotlin 复制代码
    // 扩展函数形式(配置对象并返回结果)
    val personInfo = Person("Alice", 25).run {
        name = "Bob"           // 直接修改属性(this 可省略)
        age += 1
        "Name: $name, Age: $age"  // 返回字符串
    }  // 输出:Name: Bob, Age: 26
    
    // 非扩展形式(替代临时变量)
    val fullName = run {
        val firstName = "John"
        val lastName = "Doe"
        "$firstName $lastName"     // 返回拼接结果
    }

3. ​**with:集中操作非空对象**​

  • 场景​:对同一对象执行多步操作(无需重复写对象名)。

  • 示例​:

    kotlin 复制代码
    val person = Person("Bob", 25)
    val info = with(person) {
        age = 26                   // 直接访问属性
        "Name: $name, Age: $age"   // 返回结果
    }

4. ​**apply:对象初始化配置**​

  • 场景​:构建对象时批量设置属性(返回自身,支持链式)。

  • 示例​:

    ini 复制代码
    val person = Person().apply {
        name = "Charlie"
        age = 28
        address = "Paris"
    }  // 返回已配置的 Person 对象

5. ​**also:链式调用附加操作**​

  • 场景​:日志记录、数据验证等副作用操作(不影响对象本身)。

  • 示例​:

    scss 复制代码
    mutableListOf(1, 2, 3)
        .also { println("初始列表: $it") }  // 输出:初始列表: [1, 2, 3]
        .add(4)

🛠️ ​三、进阶技巧与避坑指南

  1. 链式组合​:

    • apply + also:初始化后记录日志

      scss 复制代码
      val file = File("data.txt").apply { 
          createNewFile() 
      }.also { println("创建文件: ${it.path}") }
    • let + run:空安全转换后继续操作

      scss 复制代码
      user?.let { it.validate() }?.run { process(this) }
  2. 空安全优先策略​:

    • 可空对象操作 → ?.let {} ?: default
    • 避免 apply/also 在可空对象上直接调用(需手动判空)。
  3. 性能优化​:

    • 所有作用域函数均为 inline,无运行时开销(编译期内联优化)。

💎 ​四、总结选择流程图

graph TD A{NeedReturnSelf} A -->|Yes| B{Require This} B -->|Yes| C[apply] B -->|No| D[also] A -->|No| E{NeedNullSafe} E -->|Yes| F[let] E -->|No| G{RequireExtension} G -->|Yes| H[run] G -->|No| I[with]

通过明确操作目标 ​(配置、转换、副作用)和上下文需求​(空安全、链式、作用域隔离),可精准选用最合适的函数。

相关推荐
帅次26 分钟前
Android 高级工程师面试参考答案:架构设计、Jetpack 与 Compose
android·面试·职场和发展·架构·composer·jetpack
limingade28 分钟前
Dialer3.0智能拨号器Android版功能说明书
android·蓝牙电话·手机转sip·手机蓝牙·智能拨号器
JJay.32 分钟前
Android BLE 的 notify 和 indicate 到底有什么区别
android
橙子1991101635 分钟前
Android 异步任务和消息机制
android
被开发耽误的大厨1 小时前
5、Integer缓存池里同一个对象指的是什么?Integer 和String 内存结构逻辑完全一样?
android·java·哈希算法
NoSi EFUL9 小时前
MySQL中ON DUPLICATE KEY UPDATE的介绍与使用、批量更新、存在即更新不存在则插入
android·数据库·mysql
安小牛11 小时前
Android 开发汉字转带声调的拼音
android·java·学习·android studio
聚美智数11 小时前
企业实际控制人查询-公司实控人查询
android·java·javascript
JMchen12313 小时前
第 3 篇|Android 项目结构解析与第一个界面 —— Hello, CSDN!
android·android studio·android 零基础·android 项目结构·android 界面开发
众少成多积小致巨16 小时前
Soong构建入门
android·go·编译器