Kotlin 内联函数(inline)一篇看懂
Kotlin 内联函数(inline)一篇看懂
Kotlin 的 inline 本质上只有一句话:
把函数代码"复制"到调用处,而不是正常函数调用。
它主要解决两个问题:
- Lambda 带来的对象创建性能损耗
- Lambda 里无法直接 return 的限制
一、先理解:普通函数是怎么执行的
funtest(){println("hello")}funmain(){test()}正常情况下:
main() ↓ 调用 test() ↓ 执行 println这是一次"函数跳转"。
二、inline 做了什么
inlinefuntest(){println("hello")}funmain(){test()}编译后,效果类似:
funmain(){println("hello")}也就是:
- 不调用了,直接把代码贴进来
- 所以叫:内联(inline)
- 类似 C 语言宏展开
三、为什么 Kotlin 特别需要 inline
因为 Kotlin 大量使用Lambda,例如:
list.forEach{println(it)}实际上,{ println(it) }这个 Lambda:
- 会生成对象
- 会额外产生调用
在 Android 中:
- RecyclerView
- Flow
- Compose
- 点击事件
- 高阶函数
都会频繁创建 Lambda。如果大量使用,会产生:
- 更多 GC
- 更多对象
- 性能损耗
所以 Kotlin 官方大量使用 inline。
四、最经典案例:高阶函数
不使用 inline
funexecute(block:()->Unit){block()}调用:
execute{println("hello")}这里会生成一个 Lambda 对象,相当于new Function0()。Android 高频调用下会有损耗。
使用 inline 后
inlinefunexecute(block:()->Unit){block()}调用:
execute{println("hello")}编译后类似:
println("hello")Lambda 对象没了。
这就是inline 最大价值。
五、Android 为什么大量使用 inline
你会发现很多系统 API 全是inline:
runletapplyalsousewithforEach
因为这些函数:
- 调用频率极高
- 大量使用 Lambda
六、最容易看懂的 Android 实战
场景1:点击防抖
不用 inline
funsetSafeClick(block:()->Unit){block()}// 调用setSafeClick{login()}会创建 Lambda 对象。
用 inline
inlinefunsetSafeClick(block:()->Unit){block()}高频点击时:少对象创建,更流畅。
场景2:RecyclerView
RecyclerView bind 非常高频。
不推荐
funbind(block:()->Unit){block()}// onBindViewHolderbind{holder.tv.text=item.name}每次 bind 创建 Lambda,创建匿名类。列表滑动会产生大量对象。
推荐
inlinefunbind(block:()->Unit){block()}尤其:瀑布流、长列表、高频刷新,收益明显。
场景3:ViewBinding 扩展
Android 常见:
inlinefun<T:ViewBinding>Activity.binding(block:()->T):T{returnblock()}// 调用valbinding=binding{ActivityMainBinding.inflate(layoutInflater)}避免 Lambda 额外开销。
场景4:SharedPreferences 编辑
经典:
inlinefunSharedPreferences.edit(action:SharedPreferences.Editor.()->Unit){valeditor=edit()action(editor)editor.apply()}// 调用sp.edit{putString("name","Tom")}这是 Android 最经典 inline 实战之一。
七、inline 最重要的能力:支持 return
这是很多人真正没理解的点。
普通 Lambda 不能直接 return
funtest(block:()->Unit){block()}funmain(){test{return// 报错}}因为:return不知道该退出谁。
inline 可以
inlinefuntest(block:()->Unit){block()}funmain(){test{return}println("不会执行")}因为 inline 后,相当于:
funmain(){returnprintln("不会执行")}所以合法。这个叫:非局部返回(Non-local return)
八、这就是为什么 forEach 能 return
list.forEach{if(it==3){return}}因为:forEach是inline。
九、crossinline 是什么
有时候:inline 虽然允许 return,但函数作者不想让你 return。
inlinefuntest(crossinlineblock:()->Unit){Runnable{block()}.run()}这里:block被放到了另一个对象里,已经不是原位置执行。
所以:不能 return,否则:
test{return}逻辑会乱。
所以crossinline= 禁止非局部 return
十、noinline 是什么
inline 默认会:内联所有 Lambda 参数
但有时候,你需要:
- 保存 Lambda
- 当对象传递
- 延迟执行
这时候不能 inline。
inlinefuntest(noinlineblock:()->Unit){}意思:别内联这个 Lambda
十一、最容易记忆的口诀
| 关键字 | 作用 |
|---|---|
inline | 复制代码进调用处。减少 Lambda 对象,提升性能,支持 return |
crossinline | 允许内联,但禁止 return |
noinline | 不要内联这个 Lambda |
十二、Compose 为什么大量 inline
Jetpack Compose 极度依赖:
- Lambda
- 高阶函数
例如:
Column{// 本质全是 Lambda}所以 Compose 内部大量使用inline。
否则:UI 每次重组都会大量创建对象。
十三、什么时候适合 inline
推荐:
- 高频调用:RecyclerView、Compose、Flow、点击事件、ViewBinding
- 工具函数
- 高阶函数(函数参数里有
Lambda:block: () -> Unit)
优先考虑 inline。
十四、什么时候别乱用 inline
inline 会:增加字节码体积
因为:代码被复制了。
所以不适合:
超大函数
inlinefunveryBigFunction(){// ...}会导致 APK 变大。
普通函数(没有 Lambda)
inlinefunadd(a:Int,b:Int)=a+b意义很小。
十五、Android 面试高频回答模板
如果面试问:Kotlin inline 有什么作用?
可以这样回答:
inline 本质是编译时函数展开。主要用于高阶函数优化。
Kotlin Lambda 会生成匿名对象,在 Android 高频场景下会带来:对象创建、GC 压力。使用 inline 后:Lambda 不再生成对象,减少函数调用开销,提升性能。
另外 inline 支持非局部 return。
Kotlin 标准库大量使用 inline,例如:
let、apply、forEach、run。Android 中:RecyclerView、Compose、Flow、点击事件封装,都会大量使用 inline。
十六、最终一张图彻底理解
普通函数:
调用函数 ↓ 创建 Lambda 对象 ↓ 执行inline:
直接复制代码 ↓ 无对象 ↓ 无调用十七、推荐你真正去看的源码
非常建议你看这些源码:
letapplyrunforEachuse
你会发现几乎全是inline。
例如:
publicinlinefun<T>T.apply(block:T.()->Unit):T{block()returnthis}这会让你真正理解 Kotlin 设计哲学。
