Skip to content

高阶函数与 Lambda

源:Lambdas 与高阶函数官方参考

Kotlin 将函数视为 “一等公民” (First-class citizen)。这意味着函数可以存储在变量中、作为参数传递或作为返回值返回。这是函数式编程 (Functional Programming) 和构建声明式 UI (如 Jetpack Compose) 的基石。

函数类型 (Function Types)

定义一个变量来存储函数,就像定义整型一样简单。

kotlin
// 1. 基础类型:接收两个 Int,返回 Int
val sum: (Int, Int) -> Int = { x, y -> x + y }

// 2. 无参无返回值 (Unit)
val action: () -> Unit = { println("Do something") }

// 3. 可空函数类型 vs 返回可空的函数
// ⚠️ 注意括号的位置差异
val canBeNull: ((Int) -> Int)? = null // 整个变量可以是 null
val returnsNull: (Int) -> Int? = { null } // 函数必定存在,但返回 null

Lambda 表达式语法精髓

Lambda 是定义匿名函数的简便写法。

1. 尾随 Lambda (Trailing Lambda)

这是 Jetpack Compose 的核心法则。如果函数的最后一个参数是函数类型,你可以将 Lambda 表达式移到圆括号外部。

kotlin
// 定义
fun Button(onClick: () -> Unit) { /*...*/ }

// 调用
Button(onClick = { println("Clicked") }) // 普通写法
Button { println("Clicked") }            // 尾随 Lambda 写法 (推荐)

2. 隐式参数 it

如果 Lambda 只有一个参数且编译器能推导出来,你可以省略参数声明,用 it 代替。

kotlin
// 完整写法
ints.filter { x -> x > 0 }

// 简洁写法 (推荐)
ints.filter { it > 0 }

3. 参数解构 (Destructuring)

在处理 Map 或 Pair 时尤其有用。

kotlin
map.forEach { (key, value) ->
    println("$key -> $value")
}

4. 下划线 _ (Unused)

用于忽略不关心的参数。

kotlin
map.forEach { (_, value) -> println(value) }

带接收者的 Lambda (Lambda with Receiver)

这是构建 类型安全构建器 (Type-Safe Builders / DSL) 的魔法。 函数类型 A.() -> B 表示:在 Lambda 内部,this 指向 A 的实例。

kotlin
// init 参数看起来像普通函数,但它拥有 HTML 的上下文
fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init() // 将 html 实例作为接收者调用 Lambda
    return html
}
kotlin
html {
    // 这里的 this 是 HTML 对象
    body() // 相当于 this.body()
    head()
}

SAM 转换 (Single Abstract Method)

Kotlin 支持将 Lambda 自动转换为只有一个抽象方法的接口实现。

1. Java 接口互操作

对于 Java 定义的单方法接口(如 Runnable, OnClickListener),Kotlin 自动支持 SAM。

kotlin
// Java: void post(Runnable r);
handler.post { println("Running on thread") }

2. Kotlin 接口 (fun interface)

在 Kotlin 1.4+ 中,你需要显式标记 fun interface 才能启用 SAM 转换。

kotlin
// ✅ 这是一个函数式接口
fun interface IntPredicate {
    fun accept(i: Int): Boolean
}

// 自动将 Lambda 转换为 IntPredicate 实例
val isEven = IntPredicate { it % 2 == 0 }

闭包与变量捕获

Kotlin 的 Lambda 可以访问并 修改 闭包与外层作用域的变量(这与 Java 的 final 限制不同)。

kotlin
var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it // ✅ 直接修改外部变量
}
println(sum)

编译器底层会将 var 变量包装在 Ref 对象(如 IntRef)中,以支持跨栈帧的状态同步。