1. 普通Lambda
vs 挂起Lambda
的本质区别
1.1 普通Lambda
(同步执行)
val lambda: (Int) -> String = { it.toString() }
// 编译器生成:
class Lambda$1 : Function1<Int, String> {
override fun invoke(p1: Int): String {
return p1.toString() // 直接返回结果
}
}
- 特点:
- 同步执行:函数调用后立即返回结果
- 不能挂起:执行过程中不会暂停
- 简单映射:参数和返回类型直接对应
- 单线程执行:在调用线程上同步完成
1.2 挂起Lambda
(异步执行)
val suspendLambda: suspend (Int) -> String = {
delay(1000) // 可能挂起
it.toString()
}
// 编译器生成:
class SuspendLambda$1 : SuspendLambda, Function2<Int, Continuation<String>, Any?> {
override fun invoke(p1: Int, continuation: Continuation<String>): Any? {
// 可能返回实际结果或COROUTINE_SUSPENDED
}
}
- 特点:
- 异步执行:可能不会立即返回结果
- 可以挂起:遇到挂起点会暂停执行
CPS
转换:需要额外的Continuation
参数- 状态机:内部实现为状态机来处理挂起/恢复
2. CPS (Continuation Passing Style)
转换原理
2.1 什么是CPS
?
CPS是一种编程风格,函数不直接返回结果,而是将结果传递给一个continuation
(继续执行的回调)
// 直接风格 (Direct Style)
fun add(a: Int, b: Int): Int = a + b
// CPS风格
fun addCPS(a: Int, b: Int, cont: (Int) -> Unit) {
cont(a + b) // 将结果传递给continuation
}
2.2 Kotlin
挂起函数的CPS
转换
// 用户编写的挂起函数
suspend fun fetchUser(id: Int): User {
val response = httpCall(id) // 可能挂起
return parseUser(response)
}
// 编译器转换后的实际签名
fun fetchUser(id: Int, continuation: Continuation<User>): Any? {
// 返回User或COROUTINE_SUSPENDED
}
3. 为什么需要额外的Continuation
参数?
3.1 挂起和恢复机制
suspend fun example(): String {
println("Before delay")
delay(1000) // 挂起点
println("After delay")
return "Done"
}
// 编译后的状态机逻辑(简化)
fun example(continuation: Continuation<String>): Any? {
val sm = continuation as? ExampleStateMachine ?: ExampleStateMachine(continuation)
when (sm.label) {
0 -> {
println("Before delay")
sm.label = 1
val result = delay(1000, sm) // 传递状态机作为continuation
if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
// 如果没挂起,继续执行
}
1 -> {
println("After delay")
return "Done"
}
}
}
3.2 Continuation
的作用
interface Continuation<in T> {
val context: CoroutineContext
fun resumeWith(result: Result<T>) // 恢复执行的回调
}
Continuation
负责:- 保存执行状态:当前执行到哪个步骤
- 恢复执行:异步操作完成后继续执行
- 异常处理:传递异步操作中的异常
- 上下文管理:携带协程上下文信息
4. 详细的转换规则对比
4.1 普通Lambda
转换规则
// 原始类型:(P1, P2, ..., Pn) -> R
// 转换为:Function(n)<P1, P2, ..., Pn, R>
val lambda1: () -> Int = { 42 }
// 转换为:Function0<Int>
val lambda2: (String) -> Int = { it.length }
// 转换为:Function1<String, Int>
val lambda3: (Int, String) -> Boolean = { i, s -> i > 0 && s.isNotEmpty() }
// 转换为:Function2<Int, String, Boolean>
4.2 挂起Lambda
转换规则
// 原始类型:suspend (P1, P2, ..., Pn) -> R
// 转换为:Function(n+1)<P1, P2, ..., Pn, Continuation<R>, Any?>
val suspendLambda1: suspend () -> Int = {
delay(1000)
42
}
// 转换为:Function1<Continuation<Int>, Any?>
val suspendLambda2: suspend (String) -> Int = {
delay(1000)
it.length
}
// 转换为:Function2<String, Continuation<Int>, Any?>
val suspendLambda3: suspend (Int, String) -> Boolean = { i, s ->
delay(1000)
i > 0 && s.isNotEmpty()
}
// 转换为:Function3<Int, String, Continuation<Boolean>, Any?>
4.3 带接收者的Lambda
转换
// 普通带接收者Lambda
val receiverLambda: String.() -> Int = { this.length }
// 转换为:Function1<String, Int>
// 挂起带接收者Lambda
val suspendReceiverLambda: suspend String.() -> Int = {
delay(1000)
this.length
}
// 转换为:Function2<String, Continuation<Int>, Any?>
5. 为什么返回类型是Any
?而不是具体类型?
5.1 挂起函数的两种返回情况
suspend fun maybeSupsend(): String {
return if (someCondition) {
"immediate result" // 立即返回
} else {
delay(1000) // 挂起,稍后恢复
"delayed result"
}
}
// 编译后
fun maybeSupsend(cont: Continuation<String>): Any? {
return if (someCondition) {
"immediate result" // 返回String
} else {
// 启动异步操作,返回挂起标记
return COROUTINE_SUSPENDED // 返回特殊标记
}
}
5.2 COROUTINE_SUSPENDED
标记
// kotlin.coroutines.intrinsics
public val COROUTINE_SUSPENDED: Any = CoroutineSingletons.COROUTINE_SUSPENDED
// 使用示例
fun someFunction(): Any? {
return when {
canReturnImmediately -> "actual result"
needsToSuspend -> COROUTINE_SUSPENDED
else -> null
}
}
6. 完整的对比示例
6.1 普通Lambda
的完整流程
val lambda: (Int) -> String = { it.toString() }
// 编译生成
class Lambda$1 : Function1<Int, String> {
override fun invoke(p1: Int): String {
return p1.toString()
}
}
// 调用流程
val result = lambda(42) // 直接调用invoke,立即得到结果 "42"
6.2 挂起Lambda
的完整流程
val suspendLambda: suspend (Int) -> String = {
delay(1000)
it.toString()
}
// 编译生成
class SuspendLambda$1 : SuspendLambda, Function2<Int, Continuation<String>, Any?> {
var label = 0
var p$0: Int = 0 // 保存参数
override fun invoke(p1: Int, continuation: Continuation<String>): Any? {
val sm = create(p1, continuation) as SuspendLambda$1
return sm.invokeSuspend(Unit)
}
override fun invokeSuspend(result: Result<Any?>): Any? {
when (label) {
0 -> {
label = 1
val delayResult = delay(1000, this)
if (delayResult == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
1 -> {
return p$0.toString()
}
}
}
}
// 调用流程
// 1. 调用invoke(42, continuation)
// 2. 可能返回COROUTINE_SUSPENDED(挂起)
// 3. 异步操作完成后,通过continuation.resumeWith恢复
// 4. 最终得到结果 "42"
7. 总结:根本区别
特性 | 普通Lambda |
挂起Lambda |
---|---|---|
执行模式 | 同步,立即返回 | 异步,可能挂起 |
参数转换 | 直接映射 | 额外添加Continuation |
返回类型 | 保持原类型 | 改为Any?(支持挂起标记) |
内部实现 | 简单函数调用 | 状态机 + CPS |
继承关系 | Function接口 | SuspendLambda + Function接口 |
调用约定 | 直接调用 | CPS调用约定 |