Lambda — 是一种匿名函数,以紧凑表达式 { 参数 -> 体 } 的形式呈现。在 Kotlin 中,lambda 是将行为传递给高阶函数(higher-order functions)的主要方式,提供了简洁的语法,无需声明单独的函数。根据 Kotlin 文档(2026),典型项目中超过 70% 的高阶函数都是通过 lambda 调用的。Lambda 允许以函数式风格编写代码,语法噪音最小。
要点
Lambda(lambda 表达式)— 是一种定义为表达式而非声明的匿名函数。该术语源于 Alonzo Church(1936)的 lambda 演算。在编程中,lambda 代表一个代码块,可以作为值传递、存储在变量中并稍后调用。在 Kotlin 中,lambda 始终用花括号 { } 括起来,参数用箭头 -> 与体分隔。
Lambda 与普通函数的不同之处在于没有名称和 fun 关键字。这允许在使用点直接声明行为,而不会用额外的声明使代码变得杂乱。Kotlin 编译器从上下文中推断参数类型,因此在许多情况下可以省略类型。Lambda 是函数类型 (T) -> R 之上的语法糖:每个 lambda 都有对应的函数类型。
Lambda 在 Kotlin 中的主要应用是传递给高阶函数(higher-order functions)。标准库到处使用 lambda:filter { it > 0 }、map { it.uppercase() }、forEach { println(it) }。Lambda 还用于创建事件监听器、回调、通过 apply 和 run 创建对象配置器,与匿名类相比使代码更加声明式。
Kotlin 中lambda 表达式的完整形式:{ 参数 -> lambda_体 }。参数用逗号列出,后跟箭头和体 — 一个或多个表达式。如果体包含多行,最后一个表达式成为返回值。如果函数接受 lambda 作为最后一个参数,可以将其移出调用括号。
val sum: (Int, Int) -> Int = { a, b -> a + b }
// 从上下文推断的类型
val sumShort = { a: Int, b: Int -> a + b }
// Lambda 调用
val result = sum(5, 3) // 8
println(result)
变量 sum 以显式函数类型 (Int, Int) -> Int 声明,并用 lambda { a, b -> a + b } 初始化。对于 sumShort,类型在 lambda 内部指定,变量类型被推断。两种变体等效。调用 lambda 与调用普通函数没有区别 — 使用 sum(5, 3) 语法。无参数的 lambda 写为 { body },带一个参数的 — { it -> it * 2 } 或简单地 { it * 2 }。
当 lambda 正好接受一个参数时,Kotlin 提供隐式名称 it。这消除了在左侧声明参数的需要。程序员只需编写 lambda 体,使用 it 访问唯一参数。这种语法在集合处理链中特别方便,其中 it 表示当前元素。
val numbers = listOf(1, 2, 3, 4, 5)
// 显式参数名
val squaredExplicit = numbers.map { n -> n * n }
// 隐式 it
val squared = numbers.map { it * it }
// 使用 it 过滤
val even = numbers.filter { it % 2 == 0 }
println(squared) // [1, 4, 9, 16, 25]
在示例 map { it * it } 中,使用 it 代替声明 n ->。Kotlin 自动为 lambda 的唯一参数分配 it。如果 lambda 接受两个或更多参数,则不会创建 it — 必须显式声明所有参数。为了提高嵌套 lambda 的可读性,建议给参数赋予有意义的名称,并在简短的单行谓词中使用 it。
Trailing lambda(尾随 lambda)— Kotlin 的语法特性,允许将最后一个参数 lambda 放在函数调用的圆括号之外。这使代码类似于内置的语言构造。如果 lambda 是唯一参数,可以完全省略括号。这种方法在标准库和 DSL 中被积极使用。
// 标准调用
thread(block = {
println("来自线程的问候")
})
// Trailing lambda 版本
thread {
println("来自线程的问候")
}
// 使用 trailing lambda 的 repeat
repeat(3) { index ->
println("迭代 $index")
}
// 使用多行 lambda 的 run
val config = run {
val host = "localhost"
val port = 8080
"$host:$port"
}
Thread 函数接受 Runnable 作为最后一个参数。得益于 trailing lambda,lambda 可以放在括号之外传递,视觉上类似于代码块。Repeat 函数接受迭代次数和带索引的 lambda。Run — 是高阶函数的例子,其中多行 lambda 计算由最后一个表达式返回的值。如果 lambda 是唯一参数,可以省略 run() 周围的括号。
变量捕获(closure)— lambda 访问在外部作用域中声明的变量的能力。在 Kotlin 中,lambda 可以读取和修改 var 变量,以及从周围上下文中读取 val 变量。捕获的变量在其整个生命周期中与 lambda 一起存储,从而允许创建带状态的函数。
fun makeCounter(): () -> Int {
var count = 0
return {
count++ // 捕获次数
}
}
fun main() {
val counter = makeCounter()
println(counter()) // 0
println(counter()) // 1
println(counter()) // 2
}
MakeCounter 函数声明一个局部变量 count 并返回 lambda { count++ }。尽管 makeCounter 已完成其工作,返回的 lambda 通过闭包(closure)保持对 count 的引用。每次调用 counter() 都会增加并返回当前值。Closure 机制构成了柯里化、函数工厂和记忆化的基础。重要的是要记住,捕获可变的变量可能在并行执行中导致意外行为。
常见问题
Lambda 不支持无条件的 return — 没有标签的 return 将控制权返回给外部函数。要从 lambda 退出,请使用 return@label,其中 label 是 lambda 被传递到的那个高阶函数的名称。
匿名函数 写为 fun(a: Int): Int { return a * 2 } 并支持没有标签的 return。Lambda { a -> a * 2 } 语法上更简短,但提前退出需要 return@label。
如果 lambda 不接受参数,箭头被省略:{ body() }。如果接受一个参数但不使用它,则写 { _ -> body() } 并用下划线忽略参数。
是的,每个传递给非内联函数的 lambda 都会创建一个匿名类实例。对于内联函数,lambda 在调用点内联而不创建对象。非捕获(non-capturing)lambda 被缓存在单例对象中。
Return 在 Kotlin 中是一个跳转运算符,属于最近的带 fun 的函数。Lambda 不是函数(fun),而是表达式。因此,lambda 内部不带标签的 return 被视为从外部包围函数的返回。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。