Kotlin 内联函数

Kotlin 的 内联函数(inline function) 是一种优化手段 ,它允许在编译时将函数的代码"展开"到调用处,从而避免函数调用的开销,尤其适用于 高阶函数(接受函数作为参数) 的场景。

什么是内联函数

正常函数调用会产生函数对象、栈帧和跳转指令,但内联函数会在编译时将函数代码插入到调用点,避免这些开销。

kotlin 复制代码
inline fun doSomething(block: () -> Unit) {
    println("Before")
    block()
    println("After")
}

fun main() {
    doSomething {
        println("Hello")
    }
}

编译后大致等价于:

kotlin 复制代码
fun main() {
    println("Before")
    println("Hello")
    println("After")
}

函数体会"复制粘贴"到调用点。

什么时候使用 inline

Kotlin 中高阶函数 (比如 letrunwithapply)频繁使用内联,避免函数对象的创建(block: () -> Unit 不会生成 Function 对象),避免 lambda 的闭包捕获、堆分配和 GC 开销。普通业务函数不推荐使用内联,会导致代码膨胀。

inline 的工作原理

内联函数不是运行时特性,而是编译器替换机制inline fun foo(block: () -> Unit) 编译器看到后就会展开 block() 的实现到调用点。如果有多个调用点,会展开多次(可能造成代码膨胀)。

使用 return@label可以局部返回:

kotlin 复制代码
inline fun doSomething(block: () -> Unit) {
    block()
}

fun main() {
    doSomething {
        println("Inside block")
        return@doSomething // 局部返回,不影响 main
    }
    println("After")
}

如果不加 inline 会怎么样,编译器会生成 Function 实例(Function0),lambda 体被封装为对象,可能造成性能下降,尤其是在 Android 中。

kotlin 复制代码
fun doSomething(block: () -> Unit) {
    block()
}

noinline 与 crossinline

noinline

默认情况下,所有函数参数都会被内联。但你可以用 noinline 排除部分参数:

kotlin 复制代码
inline fun test(block1: () -> Unit, noinline block2: () -> Unit) {
    block1()  // 被内联
    block2()  // 作为函数对象
}

noinline 主要用于函数参数需要作为对象传递,以及不能内联的上下文(如存储到变量)。

kotlin 复制代码
inline fun doOps(
    inlineBlock: () -> Unit,
    noinline logging: () -> Unit  // 这里阻止了 logging 被内联
) {
    inlineBlock()   // 内联
    val func = logging  // 如果不加 noinline 会编译错误,因为不能传递被内联的 lambda
    func()
}

crossinline

禁止 lambda 用 return 做"非局部返回"。默认情况下,内联 lambda 可以直接 return,但有风险。

kotlin 复制代码
inline fun higherOrder(block: () -> Unit) {
    println("Before")
    block()
    println("After") // 如果 block 里直接 return,这行永远不会执行
}

fun test() {
    higherOrder {
        return  // 这是"非局部返回":会从 test() 函数直接返回
    }
}

要禁止这种"跳出上层函数"的行为,加 crossinline

kotlin 复制代码
inline fun higherOrder(crossinline block: () -> Unit) {
    println("Before")
    block()
    println("After") // 如果 block 里直接 return,这行永远不会执行
}

fun test() {
    higherOrder {
        return  // 现在 block() 里面不能写 return 了,这里会报错,但是依然可以写 return@higherOrder
    }
}

注意事项与风险

代码膨胀

kotlin 复制代码
inline fun heavy(block: () -> Unit) { ... }

repeat(1000) {
    heavy { ... } // 编译后生成 1000 份代码
}

函数体复杂,调用频繁时,会导致APK 变大,方法数上升。

调试困难

内联函数展开后,调试栈信息会变得混乱,调试时跳转不准。

相关推荐
敲代码的玉米C16 分钟前
我修的那个 bug,制造了另一个 bug
前端·人工智能·架构
gis开发之家25 分钟前
《Vue3 从入门到大神50篇》Vue3 源码详解(二十):生命周期钩子源码解析 —— onMounted / onUpdated 如何实现?
前端·javascript·前端框架·vue3·vue3源码
Narrastory28 分钟前
我用 Claude Code 写代码不到 2 小时,却花了 3 天做完这个软件—Vibe Coding 的正确姿势,是设计不是生成
前端·人工智能·github
Enaium29 分钟前
我实现了KMP的SDL绑定并编写了高性能软光追
前端·kotlin
古夕34 分钟前
本地正常线上白屏:一次路由切换后刷新恢复问题的排查与修复
前端·ai编程
栀鸢ouo40 分钟前
解决Element Plus表格展开行横向溢出、滚动截断问题(项目实战方案)
前端·vue.js
用户39051332192881 小时前
写了5年JS,才发现这10个方法能少写一半代码
前端
平凡的阿泽1 小时前
我用TRAE Work手搓了一个「谁是卧底」小游戏
前端·javascript
一心只读圣贤书1 小时前
AI 辅助前端视觉回归治理:从截图基线到变更解释
前端·人工智能
嘟嘟07171 小时前
用单例模式管理弹窗:从一段原生 JS 理解 Singlet
前端·javascript·代码规范