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

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.xdnf.cn/news/16672.html

相关文章:

  • Side band ECC、Inline ECC、On-die ECC、Link ECC
  • Jinja2 详细讲解
  • 基于32nm CMOS工艺的传输门D触发器设计及HSPICE仿真分析
  • 三坐标测量仪攻克深孔检测!破解新能源汽车阀体阀孔测量难题
  • 电子电气架构 --- 车载48V系统开辟全新道路
  • React组件化的封装
  • 【Kiro Code】Chat 聊天功能
  • Amazon Aurora MySQL 8.0 完整指南
  • 网络爬虫(python)入门
  • 安卓基础布局核心知识点整理
  • 嵌入式 C 语言入门:循环结构学习笔记 —— 从语法到实用技巧
  • BH1750模块
  • TransportClient详细说一说
  • ClickHouse vs PostgreSQL:数据分析领域的王者之争,谁更胜一筹?
  • Cesium 快速入门(三)Viewer:三维场景的“外壳”
  • 停更通知!
  • 数据结构-Set集合(一)Set集合介绍、优缺点
  • 【HarmonyOS】鸿蒙应用HTTPDNS 服务集成详解
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——8. AI赋能(下):在Qt中部署YOLOv8模型
  • 关系型数据库架构最优选择:基于落霞归雁思维框架的分析
  • ECMAScript2020(ES11)新特性
  • Apache HTTP Server 2.4.50 路径穿越漏洞(CVE-2021-42013)
  • 【LangChain4j 详解】Java生态大语言模型框架设计哲学与架构原理
  • Python多线程利器:重入锁(RLock)详解——原理、实战与避坑指南
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-50,(知识点:TCP/IP 模型)
  • electron开发桌面应用入门
  • Web UI自动化测试之PO篇
  • 【刷题】东方博宜oj 1307 - 数的计数
  • 域名https证书
  • 关于mysql时间类型和java model的日期类型映射