Kotlin 作用域函数详解——with

372 阅读3分钟

with 是 Kotlin 的核心作用域函数之一,‌专注于在对象上下文中批量执行操作并返回计算结果‌,通过隐式 this 访问对象成员,适用于需集中处理同一对象属性的场景14。


一、语法与核心特性

1. ‌函数签名
inline fun <T, R> with(receiver: T, block: T.() -> R): R  
  • 参数‌:

    • receiver:任意类型的对象(非空)。
    • block:带接收者的 Lambda(T.() -> R),隐式通过 this 访问对象成员。
  • 返回值‌:Lambda 最后一个表达式的结果(类型为 R)。

2. ‌核心机制
  • 隐式上下文‌:Lambda 内直接访问对象的属性和方法,无需显式 this18。
  • 集中操作‌:在同一代码块中连续调用对象的多项方法或设置属性,代码更紧凑46。
  • 内联优化‌:编译时直接将 Lambda 内容插入调用处,避免性能损耗67。

二、典型应用场景

1. ‌批量操作对象属性

对同一对象执行多个方法或属性设置,替代冗余的重复对象名书写:

val person = Person("Alice", 25)  
val info = with(person) {  
    name = "Bob"  
    age += 1  
    "姓名: $name, 年龄: $age"  // 返回最终计算结果  
}  
println(info)  // 输出:姓名: Bob, 年龄: 26  
  • 优势‌:简化对象操作的代码层级,提升可读性48。
2. ‌构建复杂字符串或集合

集中调用 StringBuilder 或集合的方法生成结果:

val list = listOf("a", "b", "c")  
val result = with(StringBuilder()) {  
    append("start\n")  
    list.forEach { append("$it\n") }  
    append("end")  
    toString()  // 返回拼接后的字符串  
}  
println(result)  
// 输出:
// start  
// a  
// b  
// c  
// end  
  • 适用性‌:适用于需要链式调用的临时对象构建场景8。
3. ‌替代临时变量

在需要表达式的位置直接操作对象,避免中间变量:

val totalLength = with(getFileList()) {  
    sumOf { it.length }  // 计算文件列表中所有文件名的总长度  
}  

三、与其他作用域函数对比

函数语法形式返回值典型场景
withwith(obj) { ... }Lambda 结果集中操作非空对象的属性或方法
runobj.run { ... }Lambda 结果对象上下文计算与初始化结合
applyobj.apply { ... }对象本身对象初始化与属性批量配置
letobj?.let { ... }Lambda 结果空安全检查与数据转换

关键区别‌:

  • with 是‌非扩展函数‌,需显式传入对象参数,而 runapply 是扩展函数16。
  • withrun 均返回 Lambda 结果,但 with 更强调对已有对象的集中操作,而 run 支持链式调用与临时作用域创建38。

四、注意事项

  1. 非空对象要求
    with 需显式传入非空对象,若对象可能为空需结合安全调用符(?.)或其他空检查逻辑处理17。

  2. 返回值明确性
    需确保 Lambda 的最后一行表达式为目标结果,避免意外返回 Unit

    // 错误示例:未明确返回值  
    val invalid = with(StringBuilder()) {  
        append("content")  // 最后一行返回 Unit  
    }  
    
    • 修正‌:显式添加返回值(如 toString())8。

总结

with 的核心价值在于‌集中处理非空对象的属性与方法调用‌,通过隐式上下文访问和灵活返回值简化代码结构,适用于需在同一作用域内批量操作对象且生成计算结果的场景