当前位置: 首页 > 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.lryc.cn/news/605632.html

相关文章:

  • 【Django】-1- 开发项目搭建
  • Django模型迁移指南:从命令用法到最佳实践
  • HttpServletRequest详细解释
  • 如何在NPM上发布自己的React组件(包)
  • 人工智能概念之十一:常见的激活函数与参数初始化
  • Cesium 快速入门(四)相机控制完全指南
  • langchain--1--prompt、output格式、LCEL示例
  • 【烧脑算法】Dijkstra 算法:解决最短路问题
  • 会议室预定系统核心技术:如何用一行SQL解决时间冲突检测难题
  • LLC电源原边MOS管DS增加RC吸收对ZVS的影响分析
  • ode with me是idea中用来干嘛的插件
  • 山东移动云主机:技术架构与特性解析
  • AI 安监系统:为工业园安全保驾护航
  • 1 机器学习概述 (第一天2025.7.31)
  • RabbitMQ 队列配置设置 RabbitMQ 消息监听器的并发消费者数量java
  • Java 大视界 -- Java 大数据在智能医疗远程健康监测与疾病预防预警中的应用(374)
  • Linux 进程管理与计划任务
  • linux git ssh配置过程
  • React中的this绑定
  • SpringMVC的核心架构与请求处理流程
  • PostgreSQL dblink 与 Spring Boot @Transactional 的事务整合
  • 网络层概述
  • AngularJS 事件
  • Web 开发 08
  • 智慧社区项目开发(四)——前后端登录认证相关功能实现解析
  • 网关 + MDC 过滤器方案,5分钟集成 日志 traceid
  • Gemini Fullstack LangGraph Quickstart(DeepSeek+Tavily版本)
  • 智慧园区通行效率↑68%!陌讯多模态融合算法的实战解析
  • 【Cpolar实现内网穿透】
  • 转码刷 LeetCode 笔记[1]:3.无重复字符的最长子串(python)