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 代码!如果你觉得有收获,欢迎点赞、收藏或转发给你的同事!🚀

相关推荐
薯条不要番茄酱1 小时前
【网络编程】从零开始彻底了解网络编程(三)
开发语言·网络·php
李艺为1 小时前
Ubuntu下展锐刷机工具spd_dump使用说明
android·linux·git·ubuntu
weixin_307779132 小时前
实现Azure Synapse Analytics安全地请求企业内部API返回数据
开发语言·python·云计算·azure
zru_96025 小时前
Java 中常用队列用法详解
java·开发语言
_一条咸鱼_6 小时前
Android Picasso 请求构建模块深度剖析(一)
android·面试·android jetpack
keep intensify6 小时前
杨氏矩阵、字符串旋转、交换奇偶位,offsetof宏
c语言·开发语言·数据结构·算法·矩阵
enyp806 小时前
c++ 类和动态内存分配
java·开发语言·c++
_一条咸鱼_6 小时前
Android Picasso 显示模块深度剖析(六)
android·面试·android jetpack
春天里的小帆船6 小时前
4.20刷题记录(单调栈)
开发语言·数据结构
hy____1237 小时前
string类(详解)
开发语言·c++