Kotlin 内联函数与block.invoke()的妙用

在 Kotlin 开发中,我们经常使用 block.invoke() 来调用函数类型的变量,但你是否知道 block() 其实是完全一样的?为什么很多人更习惯 block.invoke()?它和 Kotlin 的内联函数 inline 又有什么关系?今天,我们就从 block.invoke() 讲起,深入解析 Kotlin 内联函数的用法和优点,并探讨 block.invoke() 和 block() 之间的细节差异。


1. block.invoke() 的日常使用

我们经常会在 Kotlin 代码中看到类似这样的写法:

kotlin 复制代码
fun doSomething(block: () -> Unit) {
    println("Before block")
    block.invoke()  // 执行 block 代码块
    println("After block")
}

fun main() {
    doSomething {
        println("Inside block")
    }
}

执行结果:

复制代码
Before block
Inside block
After block

在 doSomething 这个函数里,block.invoke() 的作用就是执行 block 这个 Lambda 代码块。


2. inline 内联函数的优化

在 Kotlin 中,inline 关键字可以让 高阶函数(即参数是函数的函数)在编译时直接展开,提高运行时性能。

✅ 内联优化前

来看一个普通的高阶函数:

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

在 JVM 上,调用 doSomething 时,block 作为 一个对象 传递给 doSomething,然后 block.invoke() 在运行时执行这个对象的 invoke() 方法。这种方式会:

  • 多创建一个 Lambda 对象
  • 多一次方法调用开销

✅ 内联优化后

如果我们给 doSomething 加上 inline:

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

那么 编译器会直接把 doSomething 里的 block.invoke() 展开为:

kotlin 复制代码
fun main() {
    println("Before block")
    println("Inside block")  // block.invoke() 直接替换为代码块
    println("After block")
}

这样:

  • 避免了 Lambda 对象的创建
  • 减少了函数调用的开销
  • 提升了运行效率

因此,inline 关键字在高阶函数中非常有用,特别是当 block 频繁调用时,优化效果更明显!


3. block.invoke() 其实和 block() 是一样的

很多人写习惯了 block.invoke(),但其实你完全可以写成 block():

kotlin 复制代码
fun doSomething(block: () -> Unit) {
    println("Before block")
    block()  // 和 block.invoke() 是一样的!
    println("After block")
}

block() 其实是 block.invoke() 的语法糖,它们的作用完全相同:

kotlin 复制代码
block.invoke()  // 明确调用 invoke 方法
block()         // 语法糖,等价于上面那行

为什么很多人更喜欢 block.invoke()?

原因可能是:

  1. Kotlin 允许显式调用 invoke() ,而且大家在 IDE 提示下会看到 invoke(),就容易跟着写。

  2. 在可空情况下,大家经常写 block?.invoke() :

    kotlin 复制代码
    fun doSomething(block: (() -> Unit)?) {
        block?.invoke()  // 这里不能写 block()?,否则编译错误
    }

    因为 block?.() 这种语法在 Kotlin 里不被支持,所以 block?.invoke() 变成了大家的默认写法。


4. 什么时候用 block(),什么时候用 block?.invoke()?

写法 适用场景 说明
block() block 一定不为空 ✅ 推荐,语法最简洁
block.invoke() block 一定不为空 ✅ 功能相同,但写法稍长
block?.invoke() block 可能为空 ✅ 推荐 ,防止 NullPointerException

最佳实践:

  • 如果 block 一定不为空 ,建议直接写 block(),更简洁
  • 如果 block 可能为空 ,用 block?.invoke() 避免 NullPointerException

5. 总结

  1. block.invoke() 和 block() 完全等价 ,只是 block() 更简洁。
  2. inline 关键字能让高阶函数更高效,避免 Lambda 对象创建,减少调用开销。
  3. 如果 block 可能为空,就用 block?.invoke(),防止 NullPointerException。
  4. inline 适用于频繁调用的高阶函数,提高执行效率!

以后写代码时,你可以直接写 block(),除非 block 可能为空,那就用 block?.invoke()!😃


这篇博客的核心目标是让大家 理解 block.invoke() 的用法 ,以及 内联函数 inline 如何优化高阶函数,希望能帮助你写出更优雅的 Kotlin 代码!如果你觉得有收获,欢迎点赞、收藏或转发给你的同事!🚀

相关推荐
CarIise2 分钟前
AOP 概念的引入
java·开发语言
凉茶钱4 分钟前
多态:从虚函数语法到虚表寻址,一次讲透动态绑定
开发语言·c++·多态
Hello.Reader14 分钟前
Three.js / Cesium 卡顿怎么办?把三维渲染搬到 GPU 服务器
服务器·开发语言·javascript
Solange__21 分钟前
AI世界模型、3D场景生成和游戏引擎怎么选?按5类任务比较交互、物理与导出
开发语言·人工智能·游戏·平面·3d·自然语言处理·游戏引擎
椒颜皮皮虾྅28 分钟前
模型部署不再卡在环境上:DeploySharp 四大推理引擎与 Windows/Linux 离线交付实战
linux·运维·开发语言·人工智能·windows·计算机视觉·cuda
codebolt40 分钟前
C++程序crash分析和内存检测
开发语言·c++
椒颜皮皮虾྅41 分钟前
让 C# 读懂整张图:DeploySharp 驱动 PaddleOCR v4/v5/v6,打通 OCR 全流程
开发语言·人工智能·深度学习·学习·c#·ocr·cuda
昂好的day44 分钟前
C# 同步异步与多线程:Thread / Task / 线程池一次讲透(上篇)
开发语言·windows·c#
Java后端的Ai之路1 小时前
01、Java并发编程:学习攻略如何才能学好并发编程?
java·开发语言·学习
应用市场1 小时前
用本机跑的开源音乐模型,给安卓小游戏做 20 秒无缝循环背景音乐
android·python·游戏开发·soundpool·音频处理