8-Kotlin高阶语法-高阶函数与Lambda
Kotlin 高阶函数与 Lambda 表达式全面解析
在 Kotlin 中,函数是一等公民(First-class Citizens),这意味着函数可以像普通变量一样被存储、传递和返回。高阶函数和 Lambda 表达式正是这一特性的核心体现,它们让代码变得更加灵活、简洁和可复用。无论是集合操作、异步回调还是 DSL 构建,高阶函数与 Lambda 都扮演着关键角色。
一、核心概念
1. 函数类型(Function Type)
Kotlin 使用专门的语法来表示函数类型,格式为:(参数类型) -> 返回类型。它描述了函数的参数列表和返回值结构。
| 函数类型 | 含义 |
|---|---|
() -> Unit |
无参数,无返回值 |
(Int) -> String |
接收一个 Int,返回 String |
(Int, Int) -> Int |
接收两个 Int,返回 Int |
(String) -> (Int) -> Long |
接收 String,返回「接收 Int 返回 Long」的函数(高阶函数类型) |
特殊形式:
- 可空函数类型:
((Int, Int) -> Int)? - 带接收者的函数类型:
String.(Int) -> String(可在接收者对象上调用) - 挂起函数类型:
suspend () -> Unit(用于协程)
2. Lambda 表达式基础
Lambda 表达式是函数字面值(Function Literal),即未声明但立即作为表达式传递的函数。它是函数类型实例化最常用的方式。
基本语法:
{ 参数列表 -> 函数体 }
- 参数列表格式为
param1: Type1, param2: Type2,类型可省略(编译器自动推导) ->分隔参数和函数体- 函数体如果是单行表达式,最后一行结果自动作为返回值;如果是多行,需要显式
return(需标记标签)
基础示例:
fun main() {
// 1. 无参数 Lambda
val sayHello: () -> Unit = { println("Hello Lambda!") }
sayHello() // 输出:Hello Lambda!
// 2. 有参数 Lambda(显式指定类型)
val add: (Int, Int) -> Int = { a: Int, b: Int -> a + b }
println(add(3, 5)) // 输出:8
// 3. 简化:省略参数类型(编译器推导)
val multiply = { a: Int, b: Int -> a * b }
println(multiply(4, 6)) // 输出:24
// 4. 单行 vs 多行 Lambda
val calculate = { a: Int, b: Int ->
val sum = a + b
val product = a * b
product - sum // 多行时,最后一行是返回值
}
println(calculate(5, 3)) // 输出:15-8=7
}
二、高阶函数(Higher-Order Function)
1. 定义
高阶函数 是指满足以下任一条件的函数:
- 接收一个或多个函数类型参数
- 返回值是函数类型
2. 接收函数作为参数
// 高阶函数:接收两个 Int + 一个函数类型参数(运算逻辑)
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
println("执行运算:$a 和 $b")
return op(a, b) // 调用传入的函数逻辑
}
fun main() {
// 调用高阶函数:传入 Lambda 作为运算逻辑
val sumResult = operate(10, 5) { x, y -> x + y }
val multiplyResult = operate(10, 5) { x, y -> x * y }
val maxResult = operate(10, 5) { x, y -> if (x > y) x else y }
println("加法结果:$sumResult") // 15
println("乘法结果:$multiplyResult") // 50
println("最大值:$maxResult") // 10
}
3. 返回函数作为结果
// 高阶函数:根据操作类型,返回对应的函数逻辑
fun getOperation(type: String): (Int, Int) -> Int {
return when (type) {
"add" -> { a, b -> a + b }
"subtract" -> { a, b -> a - b }
"multiply" -> { a, b -> a * b }
else -> { a, b -> 0 }
}
}
fun main() {
// 获取加法函数
val addFunc = getOperation("add")
println(addFunc(8, 2)) // 10
// 获取减法函数
val subtractFunc = getOperation("subtract")
println(subtractFunc(8, 2)) // 6
}
4. 实际应用:集合操作
Kotlin 标准库的集合函数(如 map、filter、forEach)都是高阶函数,它们让集合处理变得异常简洁:
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6)
// 1. filter:过滤符合条件的元素(接收 (T) -> Boolean)
val evenNumbers = numbers.filter { it % 2 == 0 }
println("偶数:$evenNumbers") // [2, 4, 6]
// 2. map:转换元素(接收 (T) -> R)
val squaredNumbers = numbers.map { it * it }
println("平方数:$squaredNumbers") // [1, 4, 9, 16, 25, 36]
// 3. forEach:遍历元素(接收 (T) -> Unit)
numbers.forEach { print("$it ") } // 1 2 3 4 5 6
// 4. 链式调用(函数式编程核心)
val result = numbers
.filter { it > 2 }
.map { it * 10 }
.sum()
println("\n结果:$result") // (3+4+5+6)*10 = 180
}
三、Lambda 进阶特性
1. 单个参数的简化:it 关键字
当 Lambda 只有一个参数时,可以省略参数声明,用 it 代替:
fun main() {
val list = listOf("Apple", "Banana", "Orange")
// 完整写法
val longNames1 = list.filter { s: String -> s.length > 5 }
// 简化:省略参数类型
val longNames2 = list.filter { s -> s.length > 5 }
// 最终简化:it 关键字(推荐)
val longNames3 = list.filter { it.length > 5 }
println(longNames3) // [Banana, Orange]
}
2. 从 Lambda 返回值
- 隐式返回:最后一个表达式的值作为返回值。
- 显式返回:使用限定返回语法
return@label。
numbers.filter {
val shouldFilter = it > 0
return@filter shouldFilter // 显式返回
}
// 等价于
numbers.filter {
val shouldFilter = it > 0
shouldFilter // 隐式返回
}
3. 未使用的参数
用下划线 _ 表示不使用的参数,避免命名:
map.forEach { (_, value) -> println(value) }
4. 闭包(Closure)
Lambda 可以访问并修改其外部作用域的变量,这些变量被称为“捕获”在闭包中:
fun main() {
var total = 0
val numbers = listOf(1, 2, 3, 4)
// Lambda 捕获并修改外部变量 total
numbers.forEach { total += it }
println("总和:$total") // 10
}
与 Java 不同,Kotlin 允许修改捕获的变量(无需是 final 的)。
5. 带接收者的 Lambda(Receiver Lambda)
带接收者的函数类型允许在函数体内将接收者对象作为 this 使用,语法为 ReceiverType.(参数类型) -> 返回值类型。这是实现类型安全 DSL 的基础。
// 定义带接收者的函数类型
val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
// 调用方式1:作为扩展函数
println("abc".repeatFun(3)) // abcabcabc
// 调用方式2:传递接收者作为第一个参数
val twoParams: (String, Int) -> String = repeatFun
println(twoParams("abc", 3)) // abcabcabc
6. 作用域函数(Scope Functions)
Kotlin 标准库提供了多个实用的带接收者的高阶函数,用于在对象的上下文中执行代码块:
(1)let
- 接收者通过
it访问,返回 Lambda 结果。 - 常用于空安全处理和链式调用。
val nullableStr: String? = "Hello"
val length = nullableStr?.let {
println("字符串:$it")
it.length
} ?: 0
(2)apply
- 接收者作为
this访问,返回接收者本身。 - 常用于对象初始化配置。
data class Person(var name: String, var age: Int)
val person = Person("张三", 20).apply {
age = 25
name = "李四"
}
(3)with
- 非扩展函数,接收接收者对象和 Lambda,返回 Lambda 结果。
- 适用于对同一对象进行多次操作。
val str = "Hello Kotlin"
val result = with(str) {
println("长度:$length")
uppercase()
}
(4)run
- 结合了
with和apply的特点,可作为扩展调用,返回 Lambda 结果。
val result = str.run {
length
}
(5)also
- 类似
let,但返回接收者本身。 - 常用于附加操作,如日志、验证。
val numbers = mutableListOf(1, 2, 3).also {
println("初始化列表:$it")
it.add(4)
}
四、函数类型实例化
除了 Lambda,还有其他方式获得函数类型的实例。
1. Lambda 表达式(已介绍)
2. 匿名函数(Anonymous Functions)
匿名函数提供了一种显式指定返回类型的替代语法,与 Lambda 的主要区别在于 return 的行为:匿名函数中的 return 返回自身,而 Lambda 中的 return 返回外层函数。
// 表达式体
val sum = fun(x: Int, y: Int): Int = x + y
// 代码块体
val sum = fun(x: Int, y: Int): Int {
return x + y
}
// 示例对比
fun main() {
val list = listOf(1, 2, 3, 4)
// Lambda:return 会从 main 返回(非局部返回)
list.forEach {
if (it == 2) return // 结束 main 函数
println(it)
}
// 匿名函数:return 只结束匿名函数自身
list.forEach(fun(value: Int) {
if (value == 2) return // 仅结束匿名函数
println(value)
})
}
3. 可调用引用(Callable References)
使用 :: 运算符引用已有声明:
// 顶层函数
fun isOdd(x: Int) = x % 2 != 0
val predicate = ::isOdd
// 扩展函数
val toInt = String::toInt
// 构造函数
val regex = ::Regex
// 成员方法
class MessageParser {
fun join(list: List<String>): String = list.joinToString()
}
val parser = MessageParser()
val func = parser::join // 绑定引用
4. 不同类型函数的传递方式
| 函数类型 | 引用方式 | 示例 |
|---|---|---|
| 顶层函数 | ::functionName |
::decrypt |
| 实例方法 | instance::method |
messageParser::joinWithoutPlaceholder |
| 伴生对象方法 | ClassName::method |
MessageParser::simplyJoin |
| 对象函数 | ObjectName::method |
ParserInObject::joinWithoutComma |
5. 将函数赋值给变量
由于函数是一等公民,可以直接赋值:
val funRef = ParserInObject::joinWithoutComma
val result = joinByOperation(input, funRef)
val funLambda = { theList: List<String> -> theList.reversed() }
val result2 = joinByOperation(input, funLambda)
6. Lambda vs 匿名函数:关键区别
| 特性 | Lambda 表达式 | 匿名函数 |
|---|---|---|
| 语法 | { x -> x + 1 } |
fun(x) = x + 1 |
| 返回类型推断 | 自动推断 | 表达式体自动推断,块体需显式 |
return 含义 |
返回外层函数 | 返回自身 |
| 传递位置 | 支持尾随 Lambda 语法 | 必须放在括号内 |
| 适用场景 | 大多数情况 | 需要显式返回类型或避免非局部返回时 |
五、性能优化:内联函数(Inline Functions)
1. 问题背景
高阶函数在调用时,每次传入的 Lambda 都会被编译成一个匿名类对象(对于捕获变量的 Lambda 还会生成新对象)。频繁调用会产生运行时开销。
2. 解决方案:inline 关键字
使用 inline 修饰高阶函数,编译器会将函数体和传入的 Lambda 直接内联到调用处,消除匿名类创建和函数调用开销。
// 内联高阶函数
inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("耗时:${end - start}ms")
}
fun main() {
// 调用内联函数(无匿名类开销)
measureTime {
Thread.sleep(100)
println("逻辑执行完成")
}
}
3. noinline 与 crossinline
-
noinline:当一个高阶函数有多个函数类型参数时,可以标记某些参数不内联(例如需要将其作为对象传递时)。inline fun inlineFunc( inlineBlock: () -> Unit, noinline noInlineBlock: () -> Unit // 该参数不内联 ) { inlineBlock() noInlineBlock() } -
crossinline:用于标记内联 Lambda,禁止其中的非局部返回(即不能直接return外层函数),常用于将 Lambda 传递给另一个执行上下文(如 Runnable)。inline fun crossInlineFunc(crossinline block: () -> Unit) { val runnable = Runnable { block() // crossinline 允许 Lambda 在此处执行,但不能 return } runnable.run() }
六、完整示例:综合运用
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
// 1. 基本 Lambda
val doubled = numbers.map { it * 2 }
println("Doubled: $doubled")
// 2. 带接收者的 Lambda (apply)
val person = Person("张三", 20).apply {
age = 25
name = "李四"
}
println("Person: $person")
// 3. 自定义高阶函数
fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
val evens = numbers.customFilter { it % 2 == 0 }
println("Evens: $evens")
// 4. 返回函数的高阶函数
fun multiplier(factor: Int): (Int) -> Int = { it * factor }
val triple = multiplier(3)
println("Triple of 5: ${triple(5)}")
// 5. 闭包
var count = 0
numbers.forEach {
count += it
}
println("Sum: $count")
// 6. 内联函数示例
measureTime {
Thread.sleep(10)
println("内联函数执行")
}
}
inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("耗时:${end - start}ms")
}
data class Person(var name: String, var age: Int)
七、官方资料链接
-
Kotlin 高阶函数与 Lambda(官方英文)
https://kotlinlang.org/docs/lambdas.html -
Kotlin 高阶函数与 Lambda(中文翻译)
https://www.kotlincn.net/docs/reference/lambdas.html -
Kotlin 内联函数
https://kotlinlang.org/docs/inline-functions.html -
Kotlin 作用域函数
https://kotlinlang.org/docs/scope-functions.html -
Kotlin 标准库 API 参考
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/
总结
- 核心概念:函数类型是描述函数签名的类型系统,Lambda 是函数字面值,高阶函数是接受或返回函数的函数。
- Lambda 特性:支持
it简化、闭包、带接收者、作用域函数等,让代码更加灵活。 - 实例化方式:Lambda、匿名函数、可调用引用(
::)各有适用场景。 - 性能优化:
inline消除高阶函数调用开销,noinline和crossinline提供更精细的控制。 - 应用广泛:集合处理、异步回调、DSL 构建等处处可见高阶函数和 Lambda 的身影。
掌握这些特性,你将能够编写出更具表现力、更简洁、更高效的 Kotlin 代码,充分发挥函数式编程的优势。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)