Kotlin 的 **also
函数是一个 作用域函数,主要用于在链式调用中执行 副作用操作**(如日志记录、数据校验等),同时保留原对象并返回它。与 apply
类似,但它通过 it
引用对象,而非隐式 this
。以下是其核心特性和典型用法:
基本特性
-
函数签名:
kotlinpublic inline fun <T> T.also(block: (T) -> Unit): T
- 是
T
的扩展函数,接收一个以T
为参数的 lambda((T) -> Unit
)。 - 返回原对象本身 (即
this
),允许链式调用。
- 是
-
核心用途:
- 在对象操作流程中插入副作用代码(如日志、校验)。
- 链式调用中保留原对象,同时执行额外操作。
使用示例
示例 1:记录对象状态
scss
val list = listOf(1, 2, 3)
.also { println("初始列表: $it") } // 打印日志,返回原列表
.map { it * 2 }
.also { println("处理后列表: $it") }
示例 2:数据校验
kotlin
fun createUser(name: String, age: Int) = User(name, age)
.also {
require(it.age >= 0) { "年龄不能为负数" } // 校验年龄
}
示例 3:链式调用中修改对象
scss
val file = File("data.txt")
.also { it.createNewFile() } // 创建文件,返回 File 对象
.also { it.setWritable(true) }
与 apply
的区别
函数 | 参数形式 | 对象引用 | 典型场景 |
---|---|---|---|
**also ** |
(T) -> Unit |
it |
副作用操作(如日志、校验) |
**apply ** |
T.() -> Unit |
this |
对象属性初始化配置 |
- **
also
更强调 显式引用对象**(通过it
),适合需要操作对象但无需直接访问其属性的场景。 - **
apply
更适用于 隐式配置对象属性**(直接通过this
访问)。
关键细节
-
显式对象引用 :在
also
的 lambda 中,通过it
操作对象(可自定义参数名):scssuser.also { u -> println(u.name) // 重命名为 u,避免嵌套作用域中的 it 歧义 }
-
空安全操作 :结合
?.
安全调用,避免空指针:kotlinval nameLength = user?.name ?.also { println("非空名称: $it") } // 仅在 name 非空时执行 ?.length
-
与构造函数配合:
inival person = Person("Alice").also { it.age = 30 // 补充初始化构造函数未涵盖的属性 }
适用场景
-
调试日志:
scssdataProcessor.process(data) .also { log("处理结果: $it") } .upload()
-
链式调用中插入操作:
scssval result = listOf(1, 2, 3) .filter { it > 1 } .also { println("过滤后集合: $it") } // 中间步骤日志 .sum()
-
对象深拷贝或备份:
scssval original = mutableListOf(1, 2, 3) val copy = original.toList().also { original.clear() // 不影响 copy }
-
资源释放前操作:
scssconnection.open() .also { it.setTimeout(5000) } .use { /* 自动关闭连接 */ }
注意事项
- 避免滥用 :若仅需配置对象属性,优先用
apply
(代码更简洁)。 - 副作用管理 :确保
also
中的操作不会破坏对象状态或逻辑。 - 性能优化:作为内联函数,无额外运行时开销。
总结
-
使用
also
的场景:- 需在链式调用中插入日志、校验等副作用代码。
- 需显式引用对象(
it
)执行操作,同时保留原对象。
-
与
apply
的对比:also
:显式操作对象,强调副作用。apply
:隐式配置对象属性,强调初始化。