Kotlin 2.4.0 正式发布,快来看看有哪些更新

本文首发于公众号"Android技术圈"

昨日,JetBrains 发布了 Kotlin 2.4.0-Beta1

如果你管的是 Android 工具链、Kotlin 多平台,或者团队里已经开始碰 context receivers、注解处理、.klib 兼容问题,这个版本已经值得单独开分支验证。

先说结论

这次最有分量的变化,不是单个 API,而是 Kotlin 在三个方向同时推进:

  • • 语言层开始定型,context parameters 和注解 use-site target 相关特性正式稳定

  • • 平台层继续对齐,JVM 向 Java 26 走,Native 向 Swift Package Manager

  • • 编译器层继续统一,不同平台对 inline 的处理开始收敛

语言层,终于开始定型

如果你这两年一直在关注 Kotlin 语言演进,最容易感知到的信号就是 context parameters

它在 2.4.0-Beta1 里转为稳定,只有 context arguments 和 callable references 还没完全毕业。

对于 DSL、Compose 风格 API、依赖注入、上下文感知扩展这类场景,context parameters 会比过去那套 receiver 叠加方式更清楚。

如果你们团队之前试过 context receivers,现在就该重新看一眼迁移策略了。

官方这次还加了一个实验能力:显式传入 context arguments

这不是为了"语法更酷",而是为了解决重载分派里的歧义问题。你们如果已经遇到"只差 context 参数就开始二义性"的情况,这个方向就很实用。

官方文档给的示例就很直接,同一个 sendNotification(),可以在调用点明确指定要用哪套上下文:

bash 复制代码
class EmailSender
class SmsSender

context(emailSender: EmailSender)
fun sendNotification() {
    println("Sent email notification")
}

context(smsSender: SmsSender)
fun sendNotification() {
    println("Sent SMS notification")
}

context(defaultEmailSender: EmailSender, defaultSmsSender: SmsSender)
fun notifyUser() {
    sendNotification(emailSender = defaultEmailSender)
    sendNotification(smsSender = defaultSmsSender)
}

如果你想提前试这个能力,官方给的编译参数是:

bash 复制代码
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xexplicit-context-arguments")
    }
}

标准库和 JVM,没有花活,但很落地

标准库这次最容易直接上手的,是 UInt.toBigInteger()ULong.toBigInteger()

以前从无符号整数转 BigInteger,很多项目都得自己绕一层字符串或者写辅助函数。

现在这块总算补齐了,代码会更直接,也更少见"我只是想转个类型,结果工具函数比业务还长"的尴尬。

另一个新增能力是"判断是否已排序"的一组 API,比如 .isSorted().isSortedBy()

这类 API 看起来小,但它非常适合放进集合校验、缓存命中判断、排序前短路和测试断言里。

你们项目里有没有那种"为了确认顺序,先 sorted() 一遍再比较"的代码?这次可以顺手清掉一批了。

官方文档里给了两组很典型的示例,一组是无符号整数直转 BigInteger,一组是直接判断集合是否已经有序:

bash 复制代码
fun main() {
    val unsignedLong = Long.MAX_VALUE.toULong() + 1uL
    val unsignedInt = UInt.MAX_VALUE

    println(unsignedLong.toBigInteger()) // 9223372036854775808
    println(unsignedInt.toBigInteger())  // 4294967295
}
bash 复制代码
data class User(val name: String, val age: Int)

fun main() {
    val numbers = listOf(1, 2, 3, 4)
    println(numbers.isSorted()) // true

    val users = listOf(
        User("Alice", 24),
        User("Bob", 31),
        User("Charlie", 29),
    )
    println(users.isSortedBy(User::age)) // false
}

JVM 侧则有两个明确信号。

第一,Kotlin 编译器开始支持生成 Java 26 字节码;第二,annotations in metadata 默认开启。

前者说明 Kotlin/JVM 继续跟着 Java 版本节奏跑,后者则更偏工具生态意义。

Kotlin/Native,开始更认真地接近 iOS 生态

这次我认为最有方向感的更新,其实在 Kotlin/Native。

Kotlin Multiplatform 现在可以把 Swift packages 作为 iOS 依赖写进 Gradle 配置了。对了昨天还发布了Swift 杀进 Android,Google 和 Apple 都要失眠了,感觉他们谁也不服谁。

这意味着 KMP 团队以后在接入 iOS 侧能力时,不一定非得先绕进 CocoaPods。

对很多 iOS 团队来说,Swift Package Manager 才是今天更自然的依赖管理方式。

Kotlin 现在主动去对齐这条生态路径,本质上是在降低 KMP 和原生 iOS 协作的摩擦。

如果你们团队之前就卡在"共享代码能跑,但 iOS 依赖接入和维护特别别扭",这次更新很值得单独验证。

它不一定立刻让所有 KMP 项目变简单,但它至少说明官方正在补真正影响落地的问题,而不是只补演示型能力。

官方文档里的 Gradle 配置示例是这样的,KMP 工程已经可以直接声明 Swift package:

bash 复制代码
kotlin {
    swiftPMDependencies {
        swiftPackage(
            url = url("https://github.com/firebase/firebase-ios-sdk.git"),
            version = from("12.11.0"),
            products = listOf(
                product("FirebaseAI"),
                product("FirebaseAnalytics"),
            )
        )
    }
}

如果你们现在还靠 CocoaPods 兜底,这段配置至少说明 Kotlin 官方想把 iOS 依赖接入往 SwiftPM 这条路上带。

编译器这次给了一个很强的信号

2.4.0-Beta1 还做了一件技术上更深、但长期更关键的事:.klib 编译时的 inline 行为开始更一致。

过去 Kotlin/JVM 会在编译期把 inline 函数直接展开,但 Kotlin/Native、JS、Wasm 在 .klib 阶段的处理并不一样。

结果就是,同样叫 inline,不同平台的兼容性保证并不完全等价。

现在 Kotlin 先把"同模块内联"默认打开,作为统一行为的第一步。

这件事看上去很底层,但它影响的是库发布、二进制兼容、调试预期,以及多平台团队对 inline 的理解成本。

如果你维护的是跨平台基础库,而不是单个 App 页面,这类变化就不能等到正式版再看了。

官方文档给的例子也很直观:同模块里的 inline 函数会在 .klib 生成时先展开,跨模块的则留到后面的平台二进制阶段。

bash 复制代码
// Existing logging.klib library
inline fun logDebug(message: String) {
    println("[DEBUG] $message")
}

// Currently compiled App module
inline fun greetUser(name: String) {
    println("Hello, $name!")
}

fun main() {
    logDebug("App started") // Not inlined: declared in another module
    greetUser("Alice")      // Inlined: declared in the same module
}

编译成 .klib 之后,官方给出的伪代码大概会变成这样:

bash 复制代码
fun main() {
    logDebug("App started")

    val tmp0 = "Alice"
    println("Hello, $tmp0!")
}

写在最后

你们团队会现在就开分支试 2.4.0-Beta1,还是等 RC 再跟?评论区聊聊你们最关心的是 context parametersSwiftPM,还是 .klib 内联。

相关推荐
鹏程十八少2 小时前
10. Android Shadow是如何实现像tinker热修复动态修复so(源码解析)
android·前端·面试
踏雪羽翼2 小时前
android 使用Gemini大模型实现图片处理
android·开发语言·ai聊天·ai抠图·ai生图·gemini大模型
Kapaseker2 小时前
Android 吐槽大会:音频焦点反人类
android·kotlin
吃不胖爹2 小时前
手机连接 Android Studio 调试完整步骤
android·智能手机·android studio
蜡台2 小时前
Android Studio 高版本兼容低版本项目配置
android·ide·jdk·gradle·android studio
Android系统攻城狮2 小时前
Android tinyalsa深度解析之pcm_params_set_min调用流程与实战(一百六十九)
android·pcm·tinyalsa·音频进阶
高梦轩2 小时前
MySQL 主从复制 + 读写分离
android·数据库
cch89183 小时前
PHP vs C++:10倍性能差距的编程语言对决
android·java·开发语言
cnnews3 小时前
Termux中安装python包
android·linux·开发语言·python·安卓·termux