前言
2024 年 Kotlin 团队把 K2 编译器标记为稳定版(Stable),2025 年正式默认开启。这个动作意味着——过去几年我们一直在调试的"老 K"前端,正式退役。换来的不只是编译快一点,而是整个类型系统、解析器的重写。
这一篇不扯官方公告里的漂亮话。我把日常开发会撞到的几个分水岭摆出来:哪些代码会立刻变快、哪些写法突然报错、Compose 编译器怎么处理。
一、K2 到底换了什么
K2 不只是给 Kotlin Compiler 加了一层缓存,它把 Frontend (前端解析) 彻底换成新的架构:
最直观感受:第一次 build 还是慢,但只要有缓存,切分支后绝大部分工程能"秒编"。
二、几个会立刻影响你写法的点
2.1 智能转型(Smart Cast)更激进
K2 在赋值、if/else 分支、null 检查后的智能转型推断范围更广了:
fun handle(req: Request?) {
// K2 下可以直接把 req 当成 Request 用
if (req != null && req.method == "POST") {
// req 在这里不再是 Request?
println(req.body.length)
}
}
K1 里上面这段偶尔会要求你 req?.let { ... } 或者 !!。K2 会更"信任"你——但反过来,类型不严谨的代码也可能跳过警告,被放过。所以开启 K2 之后,建议把 detekt / ktlint 的规则重新校准一遍。
2.2 伴生对象(Companion)的可见性检查变严
K1 时代能跑通的反射访问,K2 下直接抛 IllegalAccessError。例如:
class Holder {
companion object {
internal val secret = "..."
}
}
// K2 下:编译期就会拒绝
Holdersecret
建议:在 K2 工程里不要再依赖 internal 反射。公开 API 用,私有 API 不要试图"聪明地绕过去"。
2.3 注解的处理顺序改变
KAPT 的痛,KSP 解决。但 K2 + KSP 还有个隐藏好处:注解处理可以在 FIR 里完成,意味着增量编译时修改注解不会触发全模块重编。
迁移实践:
kapt { correctErrorTypes = true }这种老配置可以删改用
ksp("com.google.dagger:hilt-android-compiler:2.51")moshi-kotlin-codegen也走 KSP 路径
2.4 sealed class 的 when 分支对 enum data 校验更严
sealed class UiState {
object Idle : UiState()
data class Data(val value: Int) : UiState()
data class Error(val code: Int, val msg: String) : UiState()
}
fun render(s: UiState) = when (s) {
is UiState.Idle -> "..."
is UiState.Data -> s.value.toString()
// K2 下:如果漏掉 Error,编译期硬报错,不再是警告
}
三、Compose Compiler 的 K2 适配
JetBrains 在 Compose 1.5 之后官方推荐把 Compose Compiler 切到 K2 模式。需要做的几件事:
// gradle.properties
kotlin.experimental.tryK2=true
kotlin.code.style=official
// build.gradle.kts
composeCompiler {
enableIntrinsicRemember = true
enableNonSkippingGroupOptimization = true
}
迁移后的实际收益:
@Composable函数的可重启粒度(restartable groups)更细,跳过不必要重组默认
Strong Skipping模式下,lambda 捕获更稳定derivedStateOf的失效判断少一次重算
但也要小心:开发期间如果 IDE 报"Composable invocations can only happen from the context of a @Composable function",不要强 build,这种提示在 K2 下基本就是真的。
四、回退与诊断
如果你升级后某些老工程编不过,先别慌。两条应急通道:
局部回退单个模块:
// build.gradle.kts
kotlin {
compilerOptions {
languageVersion.set(KotlinVersion.KOTLIN_1_9)
}
}
查看 K2 内部诊断:
./gradlew compileDebugKotlin --info 2>&1 | grep -i "k2\|fir" | head -50
K2 编译失败的时候,错误信息会比 K1 更具体——它会告诉你"哪一步 FIR 校验没过"。
五、避坑清单(来自真实迁移)
升级 Kotlin 到 2.0 之前,把 KSP 全量更新到 2.x,老 KSP 版本会冲突
kotlinx-metadata-jvm必须升级到 0.9+,否则反射注解读不到第三方注解处理器(Hilt、Room、Moshi)务必升级最新 stable
IDE 插件 Kotlin Plugin 与工程 Kotlin 版本要严格一致(差一个小版本都可能让 K2 mode 退化)
单元测试套件覆盖率 ≥70% 的工程再上 K2,否则回退成本太高
小结
K2 不是一个"升级一下重启就好"的版本,它是一次前端范式的切换。日常写法的变化主要集中在:智能转型、可见性、注解处理、sealed 类型穷尽。建议小步推进:先用 K2 编新写的模块,老模块按升级路径慢慢切。
下一篇我们要聊 Gradle 8.x 与构建优化——一旦 Kotlin 编译快了,构建链路上其他瓶颈就会被放大,特别是 Configuration Cache 和 Build Cache 的搭配。
参考阅读
Kotlin 官方博客:K2 Compiler Performance Benchmarks
Android Developers 博客:Migrating to K2
Gradle 官方文档:Configuration Cache 章节
Compose Compiler 2.0 Release Notes