Kotlin 函数式编程:lambda 表达式与高阶函数
Kotlin 支持函数式编程范式,其中 lambda 表达式 和 高阶函数 是两大核心特性。它们能让你写出更简洁、更具表达力的代码,尤其在处理集合操作、事件回调或数据转换时非常高效。以下内容将手把手教你掌握这两个关键概念,并通过实际代码示例展示如何正确使用。
理解 lambda 表达式
lambda 表达式是一种匿名函数,通常用于简短地表示一个函数逻辑,无需显式声明函数名。
定义 lambda 表达式 的基本语法如下:
{ 参数 -> 函数体 }
例如,一个接收两个整数并返回它们和的 lambda 表达式写作:
{ a: Int, b: Int -> a + b }
你也可以将其赋值给变量以便复用:
val sum = { a: Int, b: Int -> a + b }
println(sum(3, 4)) // 输出 7
当 lambda 表达式作为函数的最后一个参数时,Kotlin 允许你把它写在括号外面,这是常见的调用风格:
listOf(1, 2, 3).forEach { println(it) }
这里 it 是 lambda 表达式只有一个参数时的默认名称。
掌握高阶函数
高阶函数是指接受函数作为参数或返回函数的函数。Kotlin 标准库中大量使用了高阶函数,比如 map、filter、fold 等。
创建自己的高阶函数
定义一个高阶函数,让它接收一个函数类型的参数:
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
调用它时传入一个 lambda 表达式:
val result = operateOnNumbers(5, 3) { x, y -> x * y }
println(result) // 输出 15
注意:operation 的类型是 (Int, Int) -> Int,表示一个接收两个 Int 并返回 Int 的函数。
使用函数引用简化代码
除了 lambda,你还可以传递已有函数的引用。假设有如下命名函数:
fun multiply(x: Int, y: Int): Int = x * y
你可以这样调用高阶函数:
val result = operateOnNumbers(5, 3, ::multiply)
::multiply 是对 multiply 函数的引用,其类型与 (Int, Int) -> Int 匹配。
实战:集合操作中的函数式编程
Kotlin 的集合类(如 List、Set)提供了丰富的高阶函数,配合 lambda 表达式可实现强大的数据处理能力。
过滤与映射
过滤出偶数并平方:
val numbers = listOf(1, 2, 3, 4, 5, 6)
val squaredEvens = numbers
.filter { it % 2 == 0 } // 保留偶数
.map { it * it } // 每个元素平方
println(squaredEvens) // 输出 [4, 16, 36]
折叠计算(累加、拼接等)
计算列表所有元素之和:
val total = numbers.fold(0) { acc, value -> acc + value }
println(total) // 输出 21
fold 的第一个参数是初始值(这里是 0),lambda 中 acc 是累积值,value 是当前元素。
你也可以用更简洁的写法:
val total = numbers.sum()
但 fold 更通用,适用于自定义累积逻辑,比如拼接字符串:
val words = listOf("Kotlin", "is", "awesome")
val sentence = words.fold("") { acc, word ->
if (acc.isEmpty()) word else "$acc $word"
}
println(sentence) // 输出 "Kotlin is awesome"
避免常见陷阱
虽然 lambda 和高阶函数很强大,但使用不当会导致性能或可读性问题。
注意闭包中的变量捕获
lambda 表达式可以访问外部作用域的变量,这称为“闭包”:
var factor = 2
val multiplier = { x: Int -> x * factor }
println(multiplier(5)) // 输出 10
factor = 3
println(multiplier(5)) // 输出 15
修改外部变量会影响 lambda 的行为。如果不需要这种动态性,应避免在 lambda 中引用可变变量。
避免过度嵌套
多层 lambda 嵌套会降低可读性:
// 不推荐
data.filter { it.isActive }
.map { user ->
user.orders.filter { order -> order.total > 100 }
.map { order -> order.id }
}
拆分为多个步骤或提取为命名函数更清晰:
val validOrders = data
.filter { it.isActive }
.flatMap { user ->
user.orders.filter { it.total > 100 }
}
.map { it.id }
使用 flatMap 可以自动展平嵌套列表,避免 List<List<T>>。
性能考量:内联函数
高阶函数在运行时会创建匿名类实例(在 JVM 上),可能带来轻微性能开销。对于频繁调用的小型高阶函数,可用 inline 关键字优化:
inline fun <T> myFilter(list: List<T>, predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in list) {
if (predicate(item)) {
result.add(item)
}
}
return result
}
添加 inline 后,编译器会在调用处直接插入 lambda 的代码,避免对象分配。
但注意:不要滥用 inline。如果函数体很大或 lambda 被存储(如赋值给变量),内联反而会增大字节码体积。
实用模式总结
下表列出常用高阶函数及其典型用途:
| 函数名 | 作用 | 示例 |
|---|---|---|
map |
转换每个元素 | list.map { it.toString() } |
filter |
筛选满足条件的元素 | list.filter { it > 0 } |
find |
查找第一个匹配元素 | list.find { it.name == "Alice" } |
any/all |
判断是否存在/全部满足 | list.any { it.isExpired } |
fold |
累积计算 | list.fold(0) { acc, i -> acc + i } |
let |
安全调用并转换 | obj?.let { process(it) } |
also |
用于副作用(如日志) | obj.also { println("Got: $it") } |
这些函数组合使用,几乎可以替代传统 for 循环,使代码更声明式、更易测试。
将业务逻辑拆解为小的高阶函数组合,是写出简洁 Kotlin 代码的关键。从今天开始,在处理集合或需要回调的地方,优先考虑 lambda 表达式与标准库高阶函数。

暂无评论,快来抢沙发吧!