跳到主要内容

Kotlin Lambda 表达式

Lambda 表达式是 Kotlin 函数式编程的核心特性,它是一种简洁的匿名函数表示方式。通过 Lambda,我们可以将函数作为值传递、存储和操作,使代码更加简洁和富有表现力。

Lambda 基础

什么是 Lambda?

Lambda 表达式本质上是匿名函数——没有名称的函数:

// 普通函数
fun sum(a: Int, b: Int): Int {
return a + b
}

// Lambda 表达式
val sumLambda = { a: Int, b: Int -> a + b }

fun main() {
println(sum(1, 2)) // 3
println(sumLambda(1, 2)) // 3
println(sumLambda.invoke(1, 2)) // 3,使用 invoke 方法
}

Lambda 语法

Lambda 表达式的完整语法:

{ 参数列表 -> 函数体 }
fun main() {
// 完整语法
val add: (Int, Int) -> Int = { a: Int, b: Int -> a + b }

// 类型推断简化
val add2 = { a: Int, b: Int -> a + b }

// 省略参数类型(当类型可推断时)
val add3: (Int, Int) -> Int = { a, b -> a + b }

println(add(1, 2)) // 3
println(add2(1, 2)) // 3
println(add3(1, 2)) // 3
}

Lambda 类型

Lambda 表达式有对应的函数类型:

fun main() {
// 无参数,无返回值
val greet: () -> Unit = { println("Hello") }

// 无参数,有返回值
val getNumber: () -> Int = { 42 }

// 有参数,有返回值
val add: (Int, Int) -> Int = { a, b -> a + b }

// 可空类型
val mayReturnNull: (String) -> Int? = { str -> str.toIntOrNull() }

// 可空函数类型
var nullableFunction: ((Int) -> Int)? = null
nullableFunction = { it * 2 }
}

高阶函数

高阶函数是以函数作为参数或返回值的函数。

函数作为参数

// 接受 Lambda 作为参数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}

fun main() {
// 传入 Lambda
val sum = calculate(10, 5) { a, b -> a + b }
println(sum) // 15

// 多行 Lambda
val detailed = calculate(10, 5) { a, b ->
println("计算中: $a$b")
a * b + a - b
}
println(detailed) // 55
}

Lambda 作为最后一个参数

当 Lambda 是函数的最后一个参数时,可以将其放在括号外面:

fun main() {
// 传统写法
calculate(10, 5, { a, b -> a + b })

// Lambda 在括号外
calculate(10, 5) { a, b -> a + b }

// 如果 Lambda 是唯一参数,可以省略括号
val numbers = listOf(1, 2, 3, 4, 5)
numbers.filter { it > 3 } // 省略括号
}

函数作为返回值

// 返回函数
fun multiplier(factor: Int): (Int) -> Int {
return { number -> number * factor }
}

// 更简洁的写法
fun multiplierSimple(factor: Int): (Int) -> Int = { it * factor }

fun main() {
val double = multiplier(2)
val triple = multiplier(3)

println(double(5)) // 10
println(triple(5)) // 15

println(multiplierSimple(4)(5)) // 20
}

it 关键字

当 Lambda 只有一个参数时,可以使用 it 作为隐式参数名:

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// 完整写法
numbers.filter { x -> x > 3 }

// 使用 it 简化
numbers.filter { it > 3 }

// 更多 it 使用示例
numbers.map { it * 2 } // [2, 4, 6, 8, 10]
numbers.forEach { println(it) } // 打印每个元素
numbers.any { it > 3 } // true
numbers.all { it > 0 } // true
numbers.find { it > 3 } // 4
}

注意:虽然 it 很方便,但当 Lambda 嵌套时,为了代码清晰,建议显式命名参数:

fun main() {
val nested = listOf(listOf(1, 2), listOf(3, 4))

// 不推荐:难以理解
nested.forEach { it.forEach { println(it) } }

// 推荐:显式命名
nested.forEach { innerList ->
innerList.forEach { element ->
println(element)
}
}
}

Lambda 返回值

Lambda 表达式的最后一行代码就是其返回值:

fun main() {
// 单行 Lambda
val square = { x: Int -> x * x }
println(square(5)) // 25

// 多行 Lambda
val process = { x: Int ->
println("Processing $x")
val doubled = x * 2
val result = doubled + 1
result // 这是返回值
}

println(process(5)) // 11
}

显式返回

如果需要显式返回,使用 return@标签

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// 使用标签返回
val result = numbers.map l@{
if (it == 3) return@l 0
it * 2
}
println(result) // [2, 4, 0, 8, 10]

// 使用隐式标签(函数名)
val result2 = numbers.filter {
it > 0 && it < 4
}
println(result2) // [1, 2, 3]
}

常用高阶函数

map - 转换

map 对集合中的每个元素应用函数,返回结果列表:

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// 转换每个元素
println(numbers.map { it * 2 }) // [2, 4, 6, 8, 10]

// 类型转换
println(numbers.map { "数字: $it" })
// [数字: 1, 数字: 2, 数字: 3, 数字: 4, 数字: 5]

// 带索引
println(numbers.mapIndexed { index, value -> "$index: $value" })
// [0: 1, 1: 2, 2: 3, 3: 4, 4: 5]

// 过滤 null
val mixed = listOf("1", "abc", "2", null, "3")
println(mixed.mapNotNull { it?.toIntOrNull() }) // [1, 2, 3]
}

filter - 过滤

filter 保留满足条件的元素:

fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 过滤偶数
println(numbers.filter { it % 2 == 0 }) // [2, 4, 6, 8, 10]

// 过滤大于 5 的数
println(numbers.filter { it > 5 }) // [6, 7, 8, 9, 10]

// 带索引过滤
println(numbers.filterIndexed { index, _ -> index % 2 == 0 })
// [1, 3, 5, 7, 9]

// 过滤非 null
val withNulls = listOf(1, null, 2, null, 3)
println(withNulls.filterNotNull()) // [1, 2, 3]
}

reduce 和 fold - 聚合

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// reduce:无初始值,从第一个元素开始累加
val sum = numbers.reduce { acc, num -> acc + num }
println(sum) // 15

// fold:有初始值
val sumWithInitial = numbers.fold(10) { acc, num -> acc + num }
println(sumWithInitial) // 25

// 字符串拼接
val concatenated = numbers.fold("") { acc, num -> acc + num }
println(concatenated) // 12345

// 计算阶乘
val factorial = (1..5).fold(1) { acc, n -> acc * n }
println(factorial) // 120

// 从右向左
val rightSum = numbers.foldRight(0) { num, acc -> num + acc }
println(rightSum) // 15
}

flatMap - 扁平化

fun main() {
// 将嵌套集合扁平化
val nested = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6))
println(nested.flatten()) // [1, 2, 3, 4, 5, 6]

// flatMap:映射 + 扁平化
val words = listOf("Hello", "World")
println(words.flatMap { it.toList() })
// [H, e, l, l, o, W, o, r, l, d]

// 生成多个元素
println(listOf(1, 2, 3).flatMap { listOf(it, it * 10) })
// [1, 10, 2, 20, 3, 30]
}

sortedBy - 排序

data class Person(val name: String, val age: Int)

fun main() {
val people = listOf(
Person("张三", 25),
Person("李四", 20),
Person("王五", 30)
)

// 按属性排序
println(people.sortedBy { it.age })
// [Person(name=李四, age=20), Person(name=张三, age=25), Person(name=王五, age=30)]

// 降序
println(people.sortedByDescending { it.age })
// [Person(name=王五, age=30), Person(name=张三, age=25), Person(name=李四, age=20)]

// 多条件排序
println(people.sortedWith(compareBy({ it.age }, { it.name })))
}

groupBy - 分组

fun main() {
val words = listOf("apple", "banana", "apricot", "blueberry", "cherry")

// 按首字母分组
println(words.groupBy { it.first() })
// {a=[apple, apricot], b=[banana, blueberry], c=[cherry]}

// 分组并转换值
println(words.groupBy(
keySelector = { it.first() },
valueTransform = { it.length }
)) // {a=[5, 7], b=[6, 9], c=[6]}

// 统计每组数量
println(words.groupingBy { it.first() }.eachCount())
// {a=2, b=2, c=1}
}

函数引用

使用 :: 操作符将命名函数转换为函数类型值:

fun isEven(n: Int): Boolean = n % 2 == 0

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// 方法引用
println(numbers.map { it.toString() })
println(numbers.map(Int::toString))

// 函数引用
println(numbers.filter(::isEven)) // [2, 4]

// 类方法引用
val strings = listOf("abc", "a", "abcd")
println(strings.sortedBy(String::length)) // [a, abc, abcd]

// 构造函数引用
val createPerson = ::Person
val person = createPerson("张三", 25)
println(person) // Person(name=张三, age=25)
}

data class Person(val name: String, val age: Int)

闭包

Lambda 可以访问并修改其外部作用域的变量:

fun main() {
// 访问外部变量
var factor = 2

val multiply: (Int) -> Int = { x -> x * factor }
println(multiply(5)) // 10

// 闭包捕获变量引用
factor = 3
println(multiply(5)) // 15

// 在 Lambda 中修改外部变量
var sum = 0
listOf(1, 2, 3, 4, 5).forEach { sum += it }
println(sum) // 15

// 计数器示例
fun createCounter(): () -> Int {
var count = 0
return { ++count }
}

val counter = createCounter()
println(counter()) // 1
println(counter()) // 2
println(counter()) // 3
}

内联函数

使用 inline 关键字可以消除 Lambda 的运行时开销:

inline 关键字

// 内联函数:Lambda 代码会被内联到调用处
inline fun measureTime(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}

fun main() {
val time = measureTime {
Thread.sleep(100)
println("执行完成")
}
println("耗时: $time ms")
}

noinline

当不希望某个 Lambda 参数被内联时,使用 noinline

inline fun example(
inlined: () -> Unit,
noinline notInlined: () -> Unit
) {
inlined()
notInlined()
}

crossinline

当内联 Lambda 不能包含非局部返回时,使用 crossinline

inline fun runInThread(crossinline action: () -> Unit) {
Thread { action() }.start()
}

fun main() {
runInThread {
// 不能使用 return,因为是在另一个线程执行
println("在后台线程执行")
}
}

作用域函数

Kotlin 提供了一组作用域函数,让代码更简洁:

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

fun main() {
// let:返回 Lambda 结果,参数为 it
val length = "Hello".let {
println("处理: $it")
it.length
}
println(length) // 5

// run:返回 Lambda 结果,使用 this
val result = "Hello".run {
println("处理: $this")
uppercase()
}
println(result) // HELLO

// with:对对象执行操作,返回结果
val person = Person("张三", 25)
val info = with(person) {
"$name, $age 岁"
}
println(info) // 张三, 25 岁

// apply:配置对象,返回对象本身
val configuredPerson = Person("", 0).apply {
name = "李四"
age = 30
}
println(configuredPerson) // Person(name=李四, age=30)

// also:执行额外操作,返回对象本身
val numbers = mutableListOf(1, 2, 3).also {
println("初始列表: $it")
}.apply {
add(4)
add(5)
}
println(numbers) // [1, 2, 3, 4, 5]
}

作用域函数选择指南

函数对象引用返回值使用场景
letitLambda 结果空安全调用、转换
runthisLambda 结果计算并返回结果
withthisLambda 结果对对象执行多个操作
applythis对象本身对象配置
alsoit对象本身附加处理、链式调用

takeIf 和 takeUnless

fun main() {
val num = 10

// takeIf:满足条件返回对象,否则返回 null
val result = num.takeIf { it > 5 }
println(result) // 10

val result2 = num.takeIf { it < 5 }
println(result2) // null

// takeUnless:不满足条件返回对象,否则返回 null
val result3 = num.takeUnless { it > 5 }
println(result3) // null

val result4 = num.takeUnless { it < 5 }
println(result4) // 10

// 链式调用
val input = "Hello"
val processed = input
.takeIf { it.isNotEmpty() }
?.uppercase()
?: "EMPTY"
println(processed) // HELLO
}

小结

本章我们学习了 Kotlin Lambda 表达式的核心内容:

  1. Lambda 基础:语法、类型、调用方式
  2. 高阶函数:函数作为参数和返回值
  3. it 关键字:单参数 Lambda 的隐式参数
  4. 常用函数mapfilterreducefoldflatMap
  5. 函数引用:: 操作符
  6. 闭包:访问外部变量
  7. 内联函数inlinenoinlinecrossinline
  8. 作用域函数letrunwithapplyalso

Lambda 表达式是 Kotlin 函数式编程的基础,掌握它可以编写更简洁、更富有表现力的代码。

练习

  1. 使用 Lambda 表达式过滤列表中的偶数并计算它们的平方和
  2. 使用 map 转换元素,使用 reduce 计算总和
  3. 使用函数引用简化代码
  4. 理解 letrunapplyalso 的区别,并写出适用场景
  5. 使用 takeIf 实现条件返回