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 = 1val result = delay(1000, sm) // 傳遞狀態機作為continuationif (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED// 如果沒掛起,繼續執行}1 -> {println("After delay")return "Done"}}
}
3.2 Continuation
的作用
interface Continuation<in T> {val context: CoroutineContextfun 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_SUSPENDEDelse -> 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 = 0var p$0: Int = 0 // 保存參數override fun invoke(p1: Int, continuation: Continuation<String>): Any? {val sm = create(p1, continuation) as SuspendLambda$1return sm.invokeSuspend(Unit)}override fun invokeSuspend(result: Result<Any?>): Any? {when (label) {0 -> {label = 1val 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調用約定 |