kotlin浅谈从高阶函数到inline noinline crossinline
看完上篇大致能理解 function type 与 lambda receiver 之间的关系。下面来看一下一个高阶函数在编译之后会变成什么查看字节码Decompile可以看到高阶函数在编译之后变成了普通的java函数接下来看看如果调用这个高阶函数那它编译会是什么样子呢直接给出编译后的解释INVOKEDYNAMIC invoke()Lkotlin/jvm/functions/Function2; [ //运行时动态生成一个 Function2 对象 InlineKt.main$lambda$0(II)I // 这个对象背后调的静态方法lambda 体运行时由 LambdaMetafactory 动态生成一个实现 Function2 的类然后我进行第二次调用看看第二次调用编译后的字节码会有什么不一样区别在于//第一次 main$lambda$0 //第二次 main$lambda$1所以每写一个 lambda就多一个 INVOKEDYNAMIC就多一个静态方法。 每次调用都会动态生成或复用一个 Function2 对象。还有就是这个function2是 Kotlin 标准库里定义的一个接口专门用来表示「接收 2 个参数、返回 1 个值」的函数下面使用inline来定义高阶函数然后调用它看看会有什么不同这里完全没有 INVOKEDYNAMIC也没有 addDefined 调用可以看到和普通高阶函数区别很大INVOKEDYNAMIC 消失了 → 没有动态生成 Function2 对象addDefined 调用消失了 → 因为 inline 把 addDefined 的函数体直接贴进了 mainICONST_1 / ICONST_2 / ISTORE → 这是 addDefined 内部的代码被内联展开后出现在 main 里这就是 inline 的本质把被调函数的代码“复制粘贴”到调用处于是函数调用没了lambda 对象也没了比如随便写一个inline函数看看反编译后是什么很鲜明public static final void main() { addDefined(1, 2, InlineKt::main$lambda$0); // ← 没 inline 的还是调用 addDefined(1, 2, InlineKt::main$lambda$1); // ← 没 inline 的还是调用 int $i$f$testInline 1; // ← inline 函数的标记 int b$iv 2; int $i$f$addDefinedInline 0; int var5 0; int a $i$f$testInline b$iv; // ← 函数体直接出现在 main 里 System.out.println(a); $i$f$testInline 0; System.out.println(测试); // ← 函数体直接出现在 main 里 System.out.println(测试); System.out.println(测试); System.out.println(测试); System.out.println(测试); }由此可以看出inline对高阶函数有重要作用如果我对一个普通函数使用inline会有这样的提示编译器也告诉我们对于普通函数 “内联inline带来的性能提升微乎其微。内联最适合用在「参数是函数类型」的函数上。”对高阶函数用 inline 就是把它带来的那两样开销——FunctionN 对象 invoke 调用——彻底消掉接下来来看关于inline函数的第二点我这里写一个简单的年龄判断如果小于18lambda表达式返回no反之yes这就奇怪了为什么会在return处报错呢。这就涉及到一个概念non-local returnkotlin官方文档对于return的解释return默认从最近的包围函数封闭函数/匿名函数返回就比如下面这个没有报错因为这个return的封闭函数就是上面的ageTest()那回到上面的那这个return上面的是不是这个ageJudge,显然不是因为他只是一个函数的调用不是封闭函数那再往上找是不是return上面这个age()呢那肯定又做不到因为它传进来是个lambda表达式怎么能跳出去来return age()呢与return的定义就不符。文档里也有所提及不过正如之前的文章里讲的可以使用标签来return其实编写的时候编译器就提醒了打一个这样的标签就可以用了还有就是给函数加个inline就可以用return了但为什么会报错呢接下来我来形象化演示一下加完inline会是什么样就会理解了根据上述得到的inline相关知识会将函数体直接拷贝到调用处那么fun age(){ println(begin) println(x(1)) //调用x即把lambda拿过来 println(end) }接下来就会变成fun age(){ println(begin) it: Int- if (it18){return no } yes println(end) }这下是不是懂了加上inline后能return的原因在这里因为这里return可以找到age()了那上述的return “no”为什么报红因为它返回的是age()但age没有声明返回类型那就默认Unit把“no”删了就行了解释it18直接return由于age是:Unit不需要所有路径都 return不用担心返回条件不足。 然后lambda 里最后一行自动作为返回值既然return的是age(),那我想return字符串的话是不是给age加个:String就行了这里还是不建议age(): String 才会“条件不足”所以编译器提示加一个TODOit 18 lambda 返回 yesage() 继续走 末尾 return TODO(...)这样是可以的不过还是建议:Unit最为方便补充ok现在来了解一下noinline。我给上面的函数加上一个参数然后我再写一个高阶函数在ageJudge里调用它看起来传y参数其实没问题那为什么会报错呢那调用一下它然后形象化转换一下看看会变成什么样现在形象化把函数体拷贝过来println(begin) { println(1) //第一个function type }) println(end) { clean(println(2)) //第二个function type }会发现clean()里面直接是拷贝过来的函数体内容这样根本用不了所以编译器在写到这的时候会提示加一个noinline拷贝函数体对这个y不生效那他就可以用这个clean()(函数了下面还有个知识点是crossinline。通过上面了解了inline的非局部返回即是inline的特权也是inline的麻烦。为什么呢当我对lambda进行一个处理把调用function type放进另一个lambda里block 可能包含非局部返回return但它现在被包进了 other 这个新 lambda 里编译器没法保证 return 还能正确工作。要么加 crossinline要么别这么用crossinline 的作用给参数加一个“禁止非局部返回”的约束。加了 crossinline 后block 里就不能写 return 了既要内联又不允许非局部返回这样才能安全地转手。以上就是我对从高阶函数到inline的浅薄理解看个乐呵兄弟们

相关新闻

面试问:数据出现异常波动,如何让 Agent 自动定位根因?| 美团

面试问:数据出现异常波动,如何让 Agent 自动定位根因?| 美团

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 5:40:43 阅读更多 →
openrig:Claude Code与Codex的本地配置编排实战

openrig:Claude Code与Codex的本地配置编排实战

1. 从 openrig 这个标题说起:它到底想解决什么问题第一次看到openrig这个名字,我脑子里蹦出来的第一反应是“open rig”,也就是“开放式的设备/工具架”。结合热搜词里那一串Claude Code、Codex、YAML、Node.js,基本可以判断出&a…

2026/10/9 5:39:43 阅读更多 →
【ENSP】DHCP 基础实验(路由器作为 DHCP 服务器,接口地址池模式)

【ENSP】DHCP 基础实验(路由器作为 DHCP 服务器,接口地址池模式)

实验任务 搭建拓扑,连线并启动所有设备在路由器 G0/0/0 配置 IP,开启 DHCP 全局功能接口启用 DHCP 接口地址池,配置 DNS、排除静态保留 IPPC 改为 DHCP 自动获取,验证 IP 分配与连通性 AR 路由器 G0/0/0 → S3700 交换机 → PC1、…

2026/10/9 5:39:43 阅读更多 →

最新新闻

OKL4微内核源码深度拆解:从IPC到用户态驱动设计

OKL4微内核源码深度拆解:从IPC到用户态驱动设计

简介:OKL4 1.4.1.1 是微内核领域早期颇具代表性的发行版,适合操作系统课程学习者、嵌入式系统开发者以及想深入理解内核机理的工程师。资源以 tar.gz 压缩格式打包,整体约 58.71MB,解开后即可按目录查看完整源码结构。目前已有 94…

2026/10/9 6:01:59 阅读更多 →
Fabric超级账本构建企业级资产可信链:登记、流转、防伪、溯源一体化

Fabric超级账本构建企业级资产可信链:登记、流转、防伪、溯源一体化

简介:这是一套面向区块链开发工程师与企业级应用实践者的开源解决方案,基于Hyperledger Fabric 1.0构建,聚焦企业资产管理、交易、防伪与溯源四大核心场景,提供从底层链码到前后端一体化的完整落地参考。资源共2000个文件&#xf…

2026/10/9 6:01:59 阅读更多 →
SSM博物馆售票系统开发实战:从数据库设计到并发防超卖全解析

SSM博物馆售票系统开发实战:从数据库设计到并发防超卖全解析

做毕业设计那会儿,我抽到的题目是一个JavaWeb方向的经典项目——博物馆售票管理系统。拿到题目的第一反应是“这有什么难的”,真正动手才发现,光是一个在线选票和库存扣减的逻辑,就能让新手纠结一整天。如果你也正在做类似的SSM项…

2026/10/9 6:01:59 阅读更多 →
Claude Code接入GLM 5完整指南:环境配置与10个实战技巧

Claude Code接入GLM 5完整指南:环境配置与10个实战技巧

最近我把 GLM 5 接进了 Claude Code,直接在终端里用 Claude Code 的交互界面跑 GLM 5 的代码生成和推理。这套组合让我日常改 bug、读工程、写测试脚本的效率明显上了一个台阶,最关键的是 API 成本比默认方案可控不少。所以这篇就把整个安装配置过程从头…

2026/10/9 6:01:59 阅读更多 →
Flutter游戏迁移OpenHarmony:碰撞检测与游戏结束处理实战

Flutter游戏迁移OpenHarmony:碰撞检测与游戏结束处理实战

把一个小游戏从 Flutter 迁到 OpenHarmony 上跑通,最大的感受就是:Dart 层的逻辑基本不用动,但“碰撞检测”和“游戏结束处理”这两块,却需要重新从算法选型到工程落地都过一遍脑子。你可能会觉得,碰撞检测不就是算两个…

2026/10/9 6:01:59 阅读更多 →
从JDK到IDEA:Java与JavaScript开发环境搭建避坑指南

从JDK到IDEA:Java与JavaScript开发环境搭建避坑指南

刚带完一个新人,他抱着笔记本跑过来说环境装了三天还没跑起来。我一看,问题非常典型:JDK装了两个版本,Maven依赖一直在下载失败,npm在PowerShell底下直接报“禁止运行脚本”,IntelliJ IDEA里项目一片飘红。…

2026/10/9 6:00:58 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 13:34:55 阅读更多 →