通俗易懂 + Android 开发实战视角,来详细讲讲 Kotlin 的 内联函数(inline functions)

一、一句话理解内联函数

内联函数(inline)是一种编译期优化技巧:它把函数调用“展开”成实际代码,避免函数调用的开销,特别适合高阶函数(比如带 lambda 的函数)。

你可以把它想象成:

  • 普通函数:打电话叫别人帮你做事(有通话成本)
  • 内联函数:直接把别人做事的步骤抄到你自己的笔记本上(省去打电话)

二、为什么需要它?—— 从 lambda 的性能问题说起

在 Android 开发中,我们大量使用 lambda 表达式,比如:

list.filter { it > 5 }
button.setOnClickListener { showToast() }
viewModelScope.launch { fetchData() }

这些 lambda 在底层会被编译成 匿名内部类(Anonymous Class),每次调用都会:

  • 创建新对象(增加 GC 压力)
  • 有方法调用开销(对高频操作不友好)

👉 内联函数就是为了解决这个问题!


三、怎么用?加个 inline 关键字

// 普通高阶函数
fun myRun(block: () -> Unit) {
    block()
}

// 内联版本
inline fun myInlineRun(block: () -> Unit) {
    block()
}

区别在哪?

  • 调用 myRun { ... } → 生成一个 lambda 对象 + 调用方法
  • 调用 myInlineRun { ... }编译器直接把 { ... } 的代码“粘贴”到调用处,没有额外对象!

四、Android 实战例子 📱

场景1:自定义作用域函数(类似 run, with

你想写一个安全执行数据库操作的函数:

inline fun <T> withDatabase(block: (db: Database) -> T): T {
    val db = getDatabase()
    try {
        return block(db)
    } finally {
        db.close()
    }
}

// 使用
val user = withDatabase { db ->
    db.findUser(123)
}

✅ 加 inline 后:

  • 不会产生 lambda 对象
  • block(db) 直接变成 db.findUser(123) 插入到调用处
  • 性能更好,尤其在循环中!

场景2:封装协程作用域(避免内存泄漏)
inline fun Fragment.launchAndCollectInLifecycle(
    crossinline block: suspend CoroutineScope.() -> Unit
) {
    viewLifecycleOwner.lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            block()
        }
    }
}

// 使用(超简洁!)
launchAndCollectInLifecycle {
    viewModel.uiState.collect { updateUI(it) }
}

🔍 注意这里用了 crossinline(后面会解释)


五、关键特性 & 规则

✅ 1. 只对高阶函数有用

内联函数的主要收益来自 避免 lambda 对象创建。如果你的函数不接收 lambda,加 inline 意义不大,甚至可能让 APK 变大(代码膨胀)。

✅ 2. 默认不能作为参数传递(non-local return)
inline fun myLoop(action: (Int) -> Unit) {
    for (i in 1..3) {
        action(i)
    }
}

fun test() {
    myLoop { 
        if (it == 2) return // ❌ 这个 return 会直接退出 test()!
    }
}

这叫 non-local return(非局部返回)—— lambda 里的 return 会跳出外层函数!

如果你不希望这样,用 crossinline

inline fun myLoop(crossinline action: (Int) -> Unit) { ... }
// 现在 lambda 里不能写 return,必须用 return@myLoop
✅ 3. 不能递归(或需 noinline
inline fun factorial(n: Int): Int {
    return if (n <= 1) 1 else n * factorial(n - 1) // ❌ 编译错误!
}

因为内联是“展开”,递归会导致无限膨胀。

解决办法:把递归部分标记为 noinline,或干脆别内联。

✅ 4. noininline:选择性不内联某个 lambda
inline fun foo(
    inlineAction: () -> Unit,
    noinline otherAction: () -> Unit  // 这个不内联
) {
    inlineAction()
    otherAction() // 作为普通函数调用
}

适用场景:当某个 lambda 需要被存储(比如赋值给变量)、或作为参数传给非内联函数时。


六、Android 中哪些地方用了内联函数?

Kotlin 标准库和 Android 框架大量使用 inline,比如:

函数说明
run, let, apply, with, also作用域函数,全部内联
repeat循环工具,内联避免 lambda 开销
viewModelScope.launch协程启动,内联优化
collect(Flow)内联,提升流处理性能

你每天都在用,只是没注意到!


七、什么时候不该用 inline

情况建议
函数很大(>10 行)❌ 不要用,会导致代码膨胀(APK 变大)
不接收 lambda❌ 没收益
需要反射调用❌ 内联函数在运行时不存在
递归函数❌ 无法内联

最佳实践:只对小型、高频调用的高阶函数使用 inline


八、总结一句话

内联函数(inline)是 Kotlin 的编译期魔法,通过“代码复制”消除 lambda 的对象开销,大幅提升高阶函数性能,是 Android 开发中编写高效、简洁工具函数的秘密武器!

但记住:不是所有函数都要内联,滥用会导致 APK 膨胀。


💡 小贴士:如何判断是否该内联?

问自己两个问题:

  1. 这个函数接收 lambda 吗?
  2. 它会被频繁调用(比如在循环、UI 渲染中)吗?

如果两个都是 ✅,那很可能适合 inline

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wuwu_q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值