当前位置: 首页 > news >正文

Kotlin -> 普通Lambda vs 挂起Lambda

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调用约定
http://www.dtcms.com/a/307469.html

相关文章:

  • Astra主题WooCommerce如何添加可变产品Astra variation product
  • tplink er2260t配置vlan透传iptv
  • python学智能算法(二十九)|SVM-拉格朗日函数求解中-KKT条件理解
  • 数据结构: 双向列表
  • 银河麒麟桌面操作系统:自定义截图快捷键操作指南
  • NXP i.MX8MP GPU 与核心库全景解析
  • rapidocr_web v1.0.0发布了
  • 旧物重生,交易有温度——旧物回收二手交易小程序,让生活更美好
  • 从“碎片化”到“完美重组”:IP报文的分片艺术
  • 从遮挡难题到精准测量:激光频率梳技术如何实现深孔 3D 轮廓的 2um 级重复精度?
  • 《Java 程序设计》第 15 章 - 事件处理与常用控件
  • 【Python修仙编程】(二) Python3灵源初探(9)
  • 无人机飞控系统3D (C++)实践
  • Coze Studio概览(四)--Prompt 管理功能详细分析
  • React的基本语法和原理
  • 力扣 Pandas 挑战(6)---数据合并
  • 融媒体中心网络安全应急预案(通用技术框架)
  • 【Debian】4-‌2 Gitea搭建
  • 专业鼠标点击器,自定义间隔次数
  • 前端核心技术Node.js(五)——Mongodb、Mongoose和接口
  • [mind-elixir]Mind-Elixir 的交互增强:单击、双击与鼠标 Hover 功能实现
  • 解决宇道项目关于接收日期格式yyyy-MM-dd HH:mm:ss后端自动转为1970-01-01 00:00:00的问题
  • 思途JSP学习 0731
  • 红黑树×协程×内存序:2025 C++后端核心三体问题攻防手册
  • LeetCode Hot 100:42. 接雨水
  • MCU中的RTC(Real-Time Clock,实时时钟)是什么?
  • 聊聊接口测试依赖第三方数据测试策略
  • mysql主从搭建(docker)
  • Verilog与SytemVerilog差别
  • 【爬虫实战】使用Python和JS逆向基于webpack的游戏平台