1. 概述
Lambda 表达式 是 Kotlin 中构建简洁且富有表现力代码的重要工具。我们可以在 Lambda 表达式中使用默认的隐式参数 it,这是 Kotlin 提供的一个非常实用的语法糖。
在本文中,我们将深入理解 it 的含义,并探讨如何在实际编码中使用它提升代码可读性。
2. 问题引入
先来看一个使用 it 作为参数的 Lambda 表达式示例:
val result = listOf("Kai", "Liam", "Eric", "Kevin")
.filter { it.length == 4 }
.map { it.uppercase() }
.sortedBy { it }
assertEquals(listOf("ERIC", "LIAM"), result)
这段代码非常直观。我们从一个名字列表出发,首先筛选出长度为 4 的字符串,然后将它们转为大写并排序。每个操作都使用了一个 Lambda 表达式,其中的 it 就是默认的单参数引用。
再来看一个稍微复杂一点的例子。这次我们要对字符串进行编码处理:将每个字符替换为其 ASCII 码加 1 后的结果:
val result = listOf("Kai", "Liam", "Eric", "Kevin").map {
it.map {
(it + 1).also { log.debug("Char After: $it") }
}.joinToString(separator = "")
}
assertEquals(listOf("Lbj", "Mjbn", "Fsjd", "Lfwjo"), result)
虽然这段代码依然简洁,但由于嵌套的 Lambda 表达式,it 所代表的含义变得模糊不清。例如,it 可以是字符串、字符,甚至是处理后的字符,容易引起歧义。
接下来,我们将深入分析 it 的作用机制,并探讨如何避免这种歧义。
3. 什么是 it?
✅ 当 Lambda 表达式只有一个参数时,Kotlin 允许我们省略显式参数声明,改用隐式参数 it 来引用该参数。
我们经常在 let()
、also()
等内置函数中使用 it,但它的使用并不局限于这些场景。比如我们自定义一个扩展函数:
fun Long.calc(desc: String, operation: (Long) -> Long) = "$desc($this) = ${operation(this)}"
其中第二个参数 operation 是一个函数类型,且只有一个 Long 类型的参数。因此我们可以这样使用:
val negateOutput = 2L.calc("negate") { -it }
assertEquals("negate(2) = -2", negateOutput)
val squareOutput = 2L.calc("square") { it * it }
assertEquals("square(2) = 4", squareOutput)
这种写法让代码更简洁,尤其适合简单的操作逻辑。
4. 提升可读性的技巧
虽然 it 很方便,但在嵌套或逻辑较复杂的情况下容易引起歧义。下面介绍几种提升可读性的方法。
4.1. 使用显式参数名替代 it
✅ *Kotlin 允许我们在 Lambda 表达式中显式声明参数名,格式为 参数名 ->
,从而覆盖默认的 it。*
以字符串编码为例,我们来优化一下:
val result = listOf("Kai", "Liam", "Eric", "Kevin").map { name ->
name.map { originalChar ->
(originalChar + 1).also { resultChar -> log.debug("Char After: $resultChar") }
}.joinToString(separator = "")
}
assertEquals(listOf("Lbj", "Mjbn", "Fsjd", "Lfwjo"), result)
现在每个 Lambda 表达式都使用了明确的参数名,逻辑清晰,易于理解。
4.2. 使用解构声明提升可读性
✅ *还可以使用 Kotlin 的 解构声明 来替代 it,使代码更具可读性。*
例如,我们有如下玩家数据:
val players = listOf("Kai" to 42, "Liam" to 50, "Eric" to 27, "Kevin" to 49)
目标是生成如下格式的报告:
val expectedOutput = listOf(
"Kai's score: 42",
"Liam's score: 50",
"Eric's score: 27",
"Kevin's score: 49"
)
我们可以这样写:
val output1 = players.map { "${it.first}'s score: ${it.second}" }
assertEquals(expectedOutput, output1)
但阅读这段代码时,我们可能需要回看 players 的定义,才能理解 it.first 和 it.second 的含义。
更好的写法是使用解构声明:
val output2 = players.map { (player, score) -> "$player's score: $score" }
assertEquals(expectedOutput, output2)
这样代码自解释性更强,也更容易维护。
5. 总结
Lambda 表达式中的 it 是一个非常实用的语法糖,尤其适合单参数的简单逻辑。但在嵌套结构或逻辑较复杂时,使用 it 容易造成理解上的歧义。
✅ 推荐做法:
- 对于简单逻辑,继续使用 it 提升代码简洁性
- 对于嵌套结构或逻辑较复杂的情况,使用显式参数名或解构声明来提升可读性
合理使用 it,既能写出优雅的代码,又能避免“踩坑”。
完整示例源码可在 GitHub 上找到。