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.firstit.second 的含义。

更好的写法是使用解构声明:

val output2 = players.map { (player, score) -> "$player's score: $score" }
assertEquals(expectedOutput, output2)

这样代码自解释性更强,也更容易维护。

5. 总结

Lambda 表达式中的 it 是一个非常实用的语法糖,尤其适合单参数的简单逻辑。但在嵌套结构或逻辑较复杂时,使用 it 容易造成理解上的歧义。

推荐做法:

  • 对于简单逻辑,继续使用 it 提升代码简洁性
  • 对于嵌套结构或逻辑较复杂的情况,使用显式参数名或解构声明来提升可读性

合理使用 it,既能写出优雅的代码,又能避免“踩坑”。

完整示例源码可在 GitHub 上找到。


原始标题:The Implicit Name Of the Single Parameter in Lambda Expressions: it