Lambdas(Lambda 表达式)
Lambda 表达式生成更简洁、更易于理解的代码。
Lambda(也称为函数字面值)是一个低仪式性的函数:它没有名称,需要最少的代码来创建,并且您可以将其直接插入到其他代码中。
首先,考虑一下 map()
,它适用于像 List
这样的集合。map()
的参数是一个转换函数,该函数应用于集合中的每个元素。map()
返回一个包含所有转换后元素的新的 List
。在这里,我们将每个 List
项转换为一个用 []
括起来的 String
:
// Lambdas/BasicLambda.kt
import atomictest.eq
fun main() {
val list = listOf(1, 2, 3, 4)
val result = list.map({ n: Int -> "[$n]" })
result eq listOf("[1]", "[2]", "[3]", "[4]")
}
Lambda 是初始化 result
时大括号内的代码。参数列表与函数体由箭头 ->
(与 when
表达式中使用的箭头相同)分隔。
函数体可以是一个或多个表达式。最后一个表达式成为 lambda 的返回值。
BasicLambda.kt
显示了完整的 lambda 语法,但通常可以简化。我们通常会在需要时创建和使用 lambda,这意味着 Kotlin 通常可以推断出类型信息。在这里,n
的类型是被推断出来的:
// Lambdas/LambdaTypeInference.kt
import atomictest.eq
fun main() {
val list = listOf(1, 2, 3, 4)
val result = list.map({ n -> "[$n]" })
result eq listOf("[1]", "[2]", "[3]", "[4]")
}
Kotlin 可以判断出 n
是一个 Int
,因为 lambda 正在与 List<Int>
一起使用。
如果只有一个参数,Kotlin 会为该参数生成名为 it
的名称,这意味着我们不再需要 n ->
:
// Lambdas/LambdaIt.kt
import atomictest.eq
fun main() {
val list = listOf(1, 2, 3, 4)
val result = list.map({ "[$it]" })
result eq listOf("[1]", "[2]", "[3]", "[4]")
}
map()
可以处理任何类型的 List
。在这里,Kotlin 推断出 lambda 参数 it
的类型为 Char
:
// Lambdas/Mapping.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c', 'd')
val result =
list.map({ "[${it.toUpperCase()}]" })
result eq listOf("[A]", "[B]", "[C]", "[D]")
}
如果 lambda 是唯一的函数参数,或者是最后一个参数,您可以省略大括号周围的括号,从而产生更简洁的语法:
// Lambdas/OmittingParentheses.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c', 'd')
val result =
list.map { "[${it.toUpperCase()}]" }
result eq listOf("[A]", "[B]", "[C]", "[D]")
}
如果函数接受多个参数,则除最后一个 lambda 参数外,所有参数都必须位于括号内。例如,您可以将 joinToString()
的最后一个参数指定为 lambda。该 lambda 用于将每个元素转换为 String
,然后将所有元素连接起来:
// Lambdas/JoinToString.kt
import atomictest.eq
fun main() {
val list = listOf(9, 11, 23, 32)
list.joinToString(" ") { "[$it]" } eq
"[9] [11] [23] [32]"
}
如果要将 lambda 提供为命名参数,必须将 lambda 放在参数列表的括号内:
// Lambdas/LambdaAndNamedArgs.kt
import atomictest.eq
fun main() {
val list = listOf(9, 11, 23, 32)
list.joinToString(
separator = " ",
transform = { "[$it]" }
) eq "[9] [11] [23] [32]"
}
以下是带有多个参数的 lambda 的语法:
// Lambdas/TwoArgLambda.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c')
list.mapIndexed { index, element ->
"[$index: $element]"
} eq listOf("[0: a]", "[1: b]", "[2: c]")
}
这使用了 mapIndexed()
库函数,它将 list
中的每个元素与该元素的索引一起传递。我们在 mapIndexed()
之后应用的 lambda 需要两个参数,以匹配索引和元素(在 List<Char>
的情况下,元素是一个字符)。
如果您不使用特定的参数,您可以使用下划线来忽略它,以消除关于未使用标识符的编译器警告:
// Lambdas/Underscore.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c')
list.mapIndexed { index, _ ->
"[$index]"
} eq listOf("[0]", "[1]", "[2]")
}
请注意,可以使用 list.indices
重写 Underscore.kt
:
// Lambdas/ListIndicesMap.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c')
list.indices.map {
"[$it]"
} eq listOf("[0]", "[1]", "[2]")
}
Lambda 可以具有零个参数,此时您可以保留箭头以强调,但 Kotlin 风格指南建议省略箭头:
// Lambdas/ZeroArguments
.kt
import atomictest.*
fun main() {
run { -> trace("A Lambda") }
run { trace("Without args") }
trace eq """
A Lambda
Without args
"""
}
标准库的 run()
函数只是调用其 lambda 参数。
- -
您可以在任何使用普通函数的地方使用 lambda,但如果 lambda 变得过于复杂,通常最好定义一个命名函数,以便清晰明了,即使您只打算使用一次。
练习和解答可在 www.AtomicKotlin.com 找到。