1. 什么是inline

inline是 Kotlin 中的一个关键字,主要用于修饰函数(尤其是高阶函数)和属性访问器。

  • **核心作用: **建议编译器将被inline修饰的函数的函数体代码以及传递给它的 Lambda 表达式代码,直接嵌入(或称“复制粘贴”)到每次调用该函数的地方。
  • **目的: **避免常规函数调用的开销(如创建栈帧、跳转、返回)以及高阶函数中 Lambda 表达式的运行时开销(如创建匿名对象、间接调用)。

inline的核心目的不是为了让你的代码“更快”,而是优化由高阶函数(Higher-Order Functions)和 Lambda 表达式带来的潜在运行时开销。

2. inline的主要用途

2.1 优化高阶函数性能

  • 问题: 默认情况下,传递给高阶函数的 Lambda 在 JVM 上会被编译成一个对象(类似匿名内部类),每次调用都会涉及对象创建和方法调用,带来开销。
  • **解决方案: **使用inline修饰高阶函数。编译器会将函数体和 Lambda 代码直接展开到调用点,消除对象创建和间接调用。

QA

Q1:什么是高阶函数和 Lambda?

A:高阶函数是指接受其他函数作为参数,或者返回一个函数的函数。例如filter, map, forEach, run, apply等都是高阶函数。

Lambda 表达式: { it > 5 }, { user -> user.age > 18 } 这种形式的匿名函数。当你把一个 Lambda 传递给高阶函数时,Kotlin(在 JVM 上)通常会在运行时创建一个代表该 Lambda 的对象(类似于匿名内部类)。

Q2:为什么会有开销?

A:

  • **对象创建: **每次传递 Lambda 时,JVM 都可能创建一个新的对象。虽然现代 JVM 对短期对象的回收做了很多优化,但频繁创建和销毁对象仍然会带来一定的性能负担,尤其是在循环或性能敏感的代码路径中。
  • **方法调用: **调用 Lambda 本质上是调用该对象上的一个方法(通常是invoke),这比直接执行代码多了一层间接调用。

Q3:inline如何优化?

A:当你在一个高阶函数前面加上inline关键字时,编译器会尝试在编译时将这个函数的整个函数体以及传递给它的 Lambda 参数的代码,直接“粘贴”到每一次调用该函数的地方。

**结果:**没有了为 Lambda 创建对象的开销,也没有了间接的方法调用开销。代码变成了像你手写的一样,直接在调用点执行逻辑。

示例

// 普通高阶函数(没有 inline)
fun doCalculation(x: Int, operation: (Int) -> Int): Int {
    println("Starting calculation...")
    val result = operation(x)
    println("Calculation finished.")
    return result
}
 
fun main() {
    val number = 5
    val doubled = doCalculation(number) { it * 2 } // 这里传递的 Lambda 会产生一个对象
    println(doubled)
}
 
/*
编译后 (简化示意):
public static final void main() {
    int number = 5;
    Function1 operation = new Function1() { // 创建了 Lambda 对象
         public Integer invoke(Integer it) {
             return it * 2;
         }
    };
    int doubled = doCalculation(number, operation);
    System.out.println(doubled);
}
*/
 
// 内联高阶函数(有 inline)
inline fun doCalculationInline(x: Int, operation: (Int) -> Int): Int {
    println("Starting calculation...")
    val result = operation(x) // 这里的 operation 会在编译时被替换
    println("Calculation finished.")
    return result
}
 
fun main() {
    val number = 5
    val doubled = doCalculationInline(number) { it * 2 } // 传递的 Lambda 代码会被内联
    println(doubled)
}
 
/*
编译后 (简化示意 - 内联展开后):
public static final void main() {
    int number = 5;
    // doCalculationInline 的函数体被直接复制到这里
    System.out.println("Starting calculation...");
    // Lambda 的代码被直接复制到这里,代替了 operation(x)
    int result = number * 2;
    System.out.println("Calculation finished.");
    int doubled = result;
    System.out.println(doubled);
}
// 注意:没有创建 Function1 对象!
*/

使用inline后,编译器生成的字节码会直接包含5 * 2的逻辑,而不是调用一个额外的函数对象。

2.2 启用特定功能(附带价值)

  • 非局部返回 (Non-local Returns): 在inline函数内部的 Lambda 中,可以使用return直接从inline函数外部的调用函数中返回。这在forEach等循环中用于提前退出非常有用。
inline fun <T> List<T>.forEachWithBreak(action: (T) -> Unit) {
    for (item in this) {
        action(item)
        // 如果 action 内部想在这里退出整个 forEachWithBreak,需要 inline + return@forEachWithBreak
        // 或者 action 内部用 return(非局部返回),但这会直接退出外面的调用函数
    }
}
 
fun someFunction() {
    listOf(1, 2, 3).forEachWithBreak {
        if (it == 2) return // 这里的 return 会直接退出 someFunction
        println(it)
    }
    println("This won't be printed if 'it' reaches 2")
}
  • reified泛型: 允许在inline函数内部访问和使用泛型参数的具体类型信息(运行时类型),绕过 JVM 的泛型类型擦除。
// 普通函数无法这样做
// fun <T> genericFunction(): T { /* ... */ } // 无法在内部知道 T 的具体类型
 
// inline 函数可以
inline fun <reified T> createInstance(): T {
    return T::class.java.newInstance() // 可以获取 T 的 Class 对象
}

3. inline与普通函数

  • 普通函数:inline对于不接受 Lambda 表达式或不涉及特殊功能(如reified)的普通函数,通常不会带来性能提升。现代编译器本身就具备强大的优化能力,对于简单的函数调用,JIT(即时编译器)可能会进行内联优化。手动给普通函数加inline可能导致字节码膨胀,且不符合其设计初衷。
  • **使用场景: **inline主要用于库/框架作者创建高性能的工具函数(如集合操作、协程工具、DSL 构建器),以及特定业务场景下需要reified泛型或非局部返回时。

4. @InlineOnly注解

这是一个标准库内部使用的注解(kotlin.internal.InlineOnly),标记在标准库的某些inline函数上。

  • **作用: **指示这个inline函数只能在同样被标记为inline的函数内部使用。如果尝试在非inline函数中直接调用@InlineOnly的函数,编译器会报错。这通常用于防止inline函数的逻辑在非内联上下文中产生意外的性能或行为问题。

5. 总结

  • inline是 Kotlin 用于优化高阶函数性能和启用非局部返回、reified泛型等特性的工具。
  • 它通过将函数体和 Lambda 代码在编译时展开到调用点,来消除运行时开销。
  • 业务代码中很少直接使用inline,因为标准库的常用高阶函数(如filter, map)已经是inline 的,且普通函数调用开销微乎其微。
  • 主要使用者是库/框架开发者或需要特定功能(reified, 非局部返回)的场景。

更新: 2026-01-19 19:56:49
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/vale8qgcmpgn6weg


相关笔记