Kotlin Lambda 与高阶函数

Lambda 和高阶函数是 Kotlin 函数式编程的基石。Kotlin 把 lambda 设计得极简洁——单参数隐式 it、最后一个参数可写在括号外、标准库所有集合操作都用 lambda。配合 inline 函数,这些 lambda 几乎没有运行时开销。本篇系统讲。

1. Lambda 表达式语法

Lambda 是匿名函数——没有名字的一段代码。用 { 参数 -> 函数体 } 写:

// Lambda 表达式:用 {} 包裹的一段代码
// 完整语法:{ 参数列表 -> 函数体 }
val sum = { a: Int, b: Int -> a + b }
println(sum(3, 4))                  // 7

// 类型推断
val mul: (Int, Int) -> Int = { a, b -> a * b }
println(mul(3, 4))                  // 12

// 无参数 lambda
val greet: () -> String = { "你好!" }
println(greet())

// 单参数:用 it 隐式参数(省略参数列表)
val square: (Int) -> Int = { it * it }
println(square(5))                  // 25

// 多语句 lambda:最后一行是返回值
val complex = { x: Int ->
    val y = x * 2
    val z = y + 1
    z                              // 返回值(不是赋值语句)
}
println(complex(5))                 // 11

几个要点:

2. 高阶函数(接收函数作为参数)

高阶函数是接收函数作为参数返回函数的函数。Kotlin 标准库大量使用高阶函数:

// 高阶函数:接收函数作为参数 或 返回函数
fun calc(a: Int, b: Int, op: (Int, Int) -> Int): Int {
    return op(a, b)
}

// 用 lambda 传函数
val r1 = calc(10, 3) { x, y -> x + y }       // 13
val r2 = calc(10, 3) { x, y -> x * y }       // 30
val r3 = calc(10, 3) { x, y -> x - y }       // 7

// lambda 在【括号外】(如果它是最后一个参数)
val r4 = calc(10, 3) { x, y -> x / y }       // 3

// 函数引用 ::,把命名函数当参数传
fun add(a: Int, b: Int) = a + b
fun sub(a: Int, b: Int) = a - b
val r5 = calc(10, 3, ::add)                  // 13
val r6 = calc(10, 3, ::sub)                  // 7

关键语法:如果 lambda 是函数的最后一个参数,可以写在括号外面。这让 DSL 风格代码非常优雅:list.map { it * 2 }.filter { it > 5 }

3. 返回函数 + 闭包

函数也可以返回函数——这是函数式编程的核心特性:

// 高阶函数返回函数
fun makeMultiplier(factor: Int): (Int) -> Int {
    return { n -> n * factor }              // 捕获 factor
}

val double = makeMultiplier(2)
val triple = makeMultiplier(3)
println(double(5))                          // 10
println(triple(5))                          // 15

// 工厂模式用函数式写法
fun validator(pattern: String): (String) -> Boolean {
    val regex = Regex(pattern)
    return { input -> regex.matches(input) }
}

val isEmail = validator("^[\\w.-]+@[\\w.-]+\\.\\w+$")
println(isEmail("a@b.com"))                  // true
println(isEmail("invalid"))                  // false

// 闭包:lambda 捕获外部变量
fun counter(): () -> Int {
    var n = 0
    return { n++; n }
}
val c = counter()
println(c())                                // 1
println(c())                                // 2
println(c())                                // 3

闭包(Closure):lambda 捕获外层作用域的变量。counter() 返回的 lambda 捕获了 n,每次调用都修改它。这种"有状态的函数"是函数式编程的常见模式。

4. inline 内联函数(消除 lambda 开销)

普通 lambda 在 JVM 上编译为 Function<T> 对象,有装箱和对象创建开销。Kotlin 用 inline 关键字把函数体和 lambda 体在编译期插入调用点,消除所有开销:

// inline 函数:消除 lambda 的运行时开销
// 普通高阶函数:lambda 会编译成 Function 对象,有装箱和对象开销
// inline 函数:编译器把函数体和 lambda 体【直接插入】调用点

inline fun repeat(times: Int, action: (Int) -> Unit) {
    for (i in 0 until times) action(i)
}

repeat(3) { i -> println("第 $i 次") }
// 编译后等价于:
// for (i in 0 until 3) println("第 $i 次")
// 没有 lambda 对象创建,性能等同手写循环

// 标准库的 forEach / map / filter / let / run / apply 都是 inline
// 所以 Kotlin 用 lambda 写集合操作【几乎无运行时开销】

// noinline:阻止某个 lambda 被内联
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
    inlined()
    notInlined()
}

// crossinline:用于"非直接调用"场景
inline fun runAsync(crossinline action: () -> Unit) {
    Thread { action() }.start()
}

因为标准库的 forEachmapfilterletrunapply 全部 inline,所以用 lambda 写集合操作几乎无运行时开销——性能等同手写循环。这是 Kotlin 设计的精妙之处。

另外两个相关关键字:noinline(禁止某个 lambda 被内联)、crossinline(用于"非直接调用"场景,如线程内)。

5. 五大作用域函数(let / run / with / apply / also)

这是 Kotlin 标准库提供的最常用的五个高阶函数,Android 开发里几乎处处可见。区别在于引用对象的方式返回值:

// 5 个作用域函数(scope functions):let / run / with / apply / also
// 区别:
//   - 引用对象用 it 还是 this
//   - 返回值是对象本身 还是 lambda 结果

data class User(var name: String, var age: Int)

// 1. let:【it】+ 返回 lambda 结果
val len = "hello".let { it.length }                // 5
// 常用于非空判断:
val name: String? = "小明"
name?.let { println(it.length) }                   // 只在非空时执行

// 2. run:【this】+ 返回 lambda 结果
val greeting = "hello".run {
    println(this)                                  // hello
    uppercase()
}
println(greeting)                                  // HELLO

// 3. with:不是方法,是顶层函数【this】+ 返回 lambda 结果
val sb = StringBuilder()
val result = with(sb) {
    append("a")
    append("b")
    append("c")
    toString()
}
println(result)                                    // abc

// 4. apply:【this】+ 返回对象本身(用于配置/构建)
val user = User("小明", 20).apply {
    age = 21                                       // this.age = 21
    name = "Xiao Ming"
}
println(user)                                      // User(name=Xiao Ming, age=21)

// 5. also:【it】+ 返回对象本身(用于副作用)
val list = mutableListOf(1, 2, 3).also {
    println("初始化: $it")
}
// 初始化: [1, 2, 3]

选择指南:

记忆口诀:let/run/with 返回结果,apply/also 返回对象本身;let/also 用 it,run/with/apply 用 this

6. 集合的函数式操作

Lambda + 集合是 Kotlin 日常编程的核心。集合 API 几乎全部基于高阶函数:

// 集合的函数式操作
val nums = listOf(1, 2, 3, 4, 5, 6)

// forEach:遍历(无返回值)
nums.forEach { println(it) }

// map:一对一转换
val doubled = nums.map { it * 2 }                  // [2, 4, 6, 8, 10, 12]

// filter:过滤
val evens = nums.filter { it % 2 == 0 }            // [2, 4, 6]

// 链式调用
val result = nums
    .filter { it > 1 }
    .map { it * it }
    .sortedDescending()
    .take(3)
println(result)                                    // [36, 25, 16]

// 分组与统计
val byParity = nums.groupBy { if (it % 2 == 0) "偶" else "奇" }
println(byParity)                                  // {奇=[1, 3, 5], 偶=[2, 4, 6]}

// reduce / fold:累加
val sum = nums.reduce { acc, n -> acc + n }        // 21
val product = nums.fold(1) { acc, n -> acc * n }   // 720

// forEachIndexed:带索引
nums.forEachIndexed { i, v -> println("[$i] = $v") }

链式调用是函数式风格的精髓——读起来像"做什么"而非"怎么做"。多个操作可以无缝组合,且因为 inline,性能等同手写循环。

7. 尾递归 lambda(Fold/Reduce)

fold / reduce 是处理集合的"累加"利器,任何需要"扫描集合产生单个值"的场景都适用:

// reduce:无初始值,从第一个元素开始
val max = listOf(3, 1, 4, 1, 5, 9).reduce { acc, n -> if (acc > n) acc else n }
println(max)                            // 9

// fold:有初始值
val concatenated = listOf("a", "b", "c").fold("") { acc, s -> acc + s }
println(concatenated)                   // abc

// 计算 list 的平均值
val stats = listOf(80, 90, 75, 95).let { nums ->
    val sum = nums.sum()
    val avg = sum.toDouble() / nums.size
    val min = nums.minOrNull()
    val max = nums.maxOrNull()
    Triple(avg, min, max)
}
println(stats)                          // (85.0, 75, 95)

// 用 runningFold 生成中间结果(滑动累加)
val prefix = listOf(1, 2, 3, 4).runningFold(0) { acc, n -> acc + n }
println(prefix)                         // [0, 1, 3, 6, 10]

8. 实战:解析配置文件

fun parseConfig(text: String): Map<String, String> {
    return text
        .lineSequence()                          // 按行
        .map { it.trim() }                       // 去空白
        .filter { it.isNotEmpty() && !it.startsWith("#") }  // 去空行和注释
        .map { it.split("=", limit = 2) }        // 分割
        .filter { it.size == 2 }                 // 只保留 key=value
        .associate { it[0].trim() to it[1].trim() }  // 转 Map
}

fun main() {
    val config = parseConfig("""
        # 服务器配置
        host = localhost
        port = 8080
        debug = true

        # 数据库
        db_url = jdbc:postgres://localhost/mydb
    """.trimIndent())

    config.forEach { (k, v) -> println("$k => $v") }
}
// host => localhost
// port => 8080
// debug => true
// db_url => jdbc:postgres://localhost/mydb

这个例子完美展示函数式风格:一行流水线完成了"读、清洗、过滤、转换、聚合"。没有 for 循环、没有可变变量、每一步都是声明式的。这种代码易读、易测、易维护。

9. DSL 构建器(高阶函数的进阶用法)

Lambda + 高阶函数让 Kotlin 可以写出非常优雅的 DSL(领域特定语言)。比如 HTML 构建:

fun html(init: HtmlBuilder.() -> Unit): String {
    val builder = HtmlBuilder()
    builder.init()
    return builder.build()
}

class HtmlBuilder {
    private val content = StringBuilder()

    fun body(init: () -> String) {
        content.append("<body>").append(init()).append("</body>")
    }

    fun h1(text: String) {
        content.append("<h1>$text</h1>")
    }

    fun p(text: String) {
        content.append("<p>$text</p>")
    }

    fun build() = "<html>$content</html>"
}

val page = html {
    body {
        h1("标题")
        p("正文内容")
        "完成"
    }
}
println(page)
// <html><body><h1>标题</h1><p>正文内容</p>完成</body></html>

这种"带接收者的 lambda"(T.() -> R)是 Kotlin DSL 的核心机制——Gradle 的 Kotlin DSL、Ktor 的路由、Anko、Jetpack Compose 全基于此。

小结

这一章你掌握了 Kotlin 函数式编程的全部基础:Lambda 语法、高阶函数、函数引用、闭包、inline 内联、五大作用域函数(let/run/with/apply/also)、集合函数式操作、DSL 构建。下一篇进入 Kotlin 最强大的异步特性——协程

← 上一篇 Kotlin 接口

下一篇 Kotlin 协程

✈️💬