kotlin中的内联函数重载

kotlin 复制代码
public fun <T> compareBy(vararg selectors: (T) -> Comparable<*>?): Comparator<T> {
    require(selectors.size > 0)
    return Comparator { a, b -> compareValuesByImpl(a, b, selectors) }
}

@kotlin.internal.InlineOnly
public inline fun <T> compareBy(crossinline selector: (T) -> Comparable<*>?): Comparator<T> =
    Comparator { a, b -> compareValuesBy(a, b, selector) }

这两个函数不冲突吗?方法名是一样的,当只传一个selector时,为什么会直接命中接收vararg selectors的那个?

这是一个很经典的 Kotlin 函数重载 + Lambda/Vararg 调用解析问题,我们来仔细分析。

1️⃣ 为什么不会冲突?

Kotlin 的重载解析规则:

  1. 非内联函数内联函数 本身不是冲突的,重载只看 函数签名

  2. 关键在于调用者传参类型和数量:

    • vararg selectors: (T) -> Comparable<*>? 可以接收 0 个或多个 Lambda
    • selector: (T) -> Comparable<*>? 只能接收 正好 1 个 Lambda
  3. 当你只传一个 Lambda 时,Kotlin 编译器会优先匹配 最具体的函数,也就是单参数版本:

    val comp = compareBy<User> { it.age }

  • 单参数 inline 版本正好匹配 (T) -> Comparable<*>?
  • vararg 版本虽然也能接收一个 Lambda(因为 vararg 可以是一个元素),但它"笼统",不如单参数版本具体
  • Kotlin 编译器规则:最具体的匹配优先

所以只传一个 Lambda 时,会命中 inline 的那个


2️⃣ 如果传多个 Lambda

复制代码
val comp = compareBy<User>({ it.age }, { it.name })
  • 这里就会命中 vararg 版本,因为单参数版本只接受一个 Lambda,而你传了两个
  • 这样就可以实现多级排序(先按 age 排,再按 name 排)

@kotlin.internal.InlineOnly 注解功能

@kotlin.internal.InlineOnly 是 Kotlin 标准库里一个内部注解 ,专门用来标记某些函数只能内联使用,不能像普通函数那样在编译后的字节码中被调用。

我们来拆开讲:


1️⃣ 作用

  • 标记函数只能内联(inline-only)
  • 编译器会在字节码中不生成真实方法 ,调用方必须在编译时展开函数体
  • 如果你尝试用反射或通过 Java 调用这个方法,会报错,因为字节码里根本没有方法实现

简单理解:

"这个函数没有实际方法存在,必须内联展开才能使用"


2️⃣ 典型用法

标准库里很多小工具函数都会用这个:

复制代码
@kotlin.internal.InlineOnly
public inline fun <T> checkNotNull(value: T?, lazyMessage: () -> Any): T {
    if (value == null) throw IllegalStateException(lazyMessage().toString())
    return value
}

特点:

  1. inline
  2. @InlineOnly
  3. 编译后 不会生成 checkNotNull 方法
  4. 只在 Kotlin 调用点展开

3️⃣ 为什么要用 InlineOnly

  • 性能优化:小函数避免创建方法调用和 Lambda 对象
  • 限制误用:防止 Java 或 Kotlin 通过反射调用不存在的方法
  • 避免冗余字节码:对于只有一行逻辑的小函数,没必要生成真实方法

4️⃣ 与普通 inline 的区别

特性 inline @InlineOnly
编译后生成方法 会生成(除非没有被引用) 绝对不会生成
Java 可见性 可见(像普通方法) 不可见,反射访问会报错
使用限制 可以被调用 只能内联使用

5️⃣ 实战感受

  • Kotlin 标准库里,绝大多数一行工具函数checkNotNullrequirecompareBy 等)都会加这个注解
  • 你在 IDE 里一般不会手动用到它,因为它属于 kotlin.internal,是内部实现细节

💡 总结一句话:

@InlineOnly = "这个函数没有真实方法实现,必须在调用点内联展开,否则无法使用"

inlinecrossinline功能

🔹 一、inline 的作用

✅ 1. 本质:函数内联(消除函数调用 + Lambda对象)

加了 inline 后,这个函数在编译时会被直接展开到调用处,而不是生成一个真实的方法调用。

👉 举个例子

调用:

复制代码
val comparator = compareBy<User> { it.age }

如果没有 inline,大致会变成:

复制代码
new Comparator<User>() {
    public int compare(User a, User b) {
        return compareValuesBy(a, b, selector); // selector是一个对象
    }
}

👉 会产生:

  • 一个 Function 对象(selector)
  • 一次函数调用

🚀 有了 inline 后:

编译器会直接"展开"代码,类似:

复制代码
Comparator { a, b ->
    compareValuesBy(a, b) { it.age }
}

甚至进一步优化(避免对象创建)

👉 好处:

  • ❌ 不创建 Lambda 对象
  • ❌ 减少函数调用开销
  • ✅ 性能更高(尤其在集合操作中)

✅ 2. 为什么 compareBy 要 inline?

因为它是一个高频工具函数(排序用得非常多),而且:

  • 参数是 Lambda(selector)
  • 很容易产生大量对象

👉 所以内联可以显著优化性能


🔹 二、crossinline 的作用

这个就更关键了 👇

✅ 1. 限制:禁止"非局部返回"(non-local return)

在 Kotlin 中:

👉 普通 inline Lambda 是可以这样写的:

复制代码
fun test() {
    listOf(1, 2, 3).forEach {
        if (it == 2) return   // ✅ 直接return test()
    }
}

👉 这是 Kotlin 的一个特性:
Lambda 里的 return 可以直接返回外层函数


❗ 但在 compareBy 这里不允许!

因为 selector 会在 另一个对象(Comparator)内部执行

复制代码
Comparator { a, b -> ... }

👉 也就是说:

  • Lambda 并不是"直接调用"
  • 而是被"包裹"到另一个对象中延迟执行

🚨 如果允许 non-local return 会发生什么?

复制代码
compareBy<User> {
    if (it.age < 0) return   // ❌ 想直接return外层函数
    it.age
}

👉 这里会出问题:

  • selector 不是在当前函数栈执行
  • return 根本不知道该返回到哪里

👉 编译器必须禁止这种行为


✅ 所以用 crossinline

复制代码
crossinline selector: (T) -> Comparable<*>?

👉 含义:

"这个 Lambda 会被放到别的地方执行,所以禁止你在里面写 return 跳出外层函数"


🔥 三、总结一句话

  • inline 👉 性能优化(展开函数 + 消除Lambda对象)
  • crossinline 👉 安全限制(禁止非局部return)

🧠 四、再帮你用一句话记忆

你可以这样记:

  • inline:👉 "我帮你展开代码,加速执行"
  • crossinline:👉 "但你别乱 return,我控制不了"

⚠️ 五、什么时候必须用 crossinline

当 Lambda:

✅ 被:

  • 存到变量中
  • 传给另一个 Lambda
  • 放进对象(比如 Runnable / Comparator

👉 就必须 crossinline


✅ 六、对你这个例子的本质理解

复制代码
Comparator { a, b -> compareValuesBy(a, b, selector) }

👉 selector 实际上:

  • ❌ 不是直接执行
  • ✅ 是"被转交"给 Comparator 里的逻辑

所以:

  • 需要 inline 提升性能
  • 需要 crossinline 防止非法 return
相关推荐
华科大胡子2 个月前
《Effective C++》学习笔记:条款02
c++·编程语言·inline·const·enum·define
派大星爱吃猫5 个月前
C++中的inline函数(内联函数)
c++·inline·内联函数
氦客1 年前
kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?
android·开发语言·kotlin·inline·noinline·crossinline·编译后的代码
egoist20231 年前
【C++】基础入门(详解)
开发语言·c++·函数重载·引用·inline·nullptr·缺省参数
小米里的大麦2 年前
【C++】内联函数(inline function)详解
c++·笔记··inline·内联函数
GarryLau2 年前
内联、普通函数、虚函数调用开销比较
c++·性能优化·inline
许野平2 年前
Rust 的 inline 内联编译策略
rust·inline·内联
Byyyi耀2 年前
Hive 行列转换
数据仓库·hive·inline·行列转换·行转列·explode·列传行
s_nshine3 年前
Kotlin 高阶函数详解
java·kotlin·inline·高阶·高阶函数