内容提要
Kotlin 2.5.0-Beta1 推出实验性“分离编译”方案,解决 KMP 项目中 IDE 与编译器不一致的问题,如 common 代码可能意外调用平台声明、重载解析和类型推断出错。启用后编译器严格按元数据 KLIB 解析 common 代码,与 IDE 一致,并支持 common 源集增量编译。需在 gradle.properties 中设置 kotlin.kmp.separateCompilation=true,注意 cinterop 等已知问题。
延伸解读
分离编译如何解决 IDE 与编译器不一致
当前 KMP 编译中,common 代码会针对依赖的平台构件(如 JVM JAR)编译,导致 common 代码可能意外解析到平台声明,而 IDE 基于元数据 KLIB 分析,认为这些声明不可见,从而出现 IDE 报错但编译通过的情况。分离编译让编译器严格按元数据 KLIB 解析 common 代码,与 IDE 行为一致,消除了重载解析、类型推断等分歧,使编译结果更可预测。
启用分离编译的注意事项与已知问题
分离编译是 Kotlin 2.5.0-Beta1 的实验性功能,默认关闭,需在 gradle.properties 中设置 kotlin.kmp.separateCompilation=true 启用。启用后编译器更严格,旧代码可能需要调整,例如为平台声明添加 expect/actual。已知问题包括 cinterop 的 commonization 问题(如 KT-88178、KT-41509),使用 C 互操作的项目需谨慎;仅声明单一目标的 KMP 模块暂不受影响。
对库作者和项目结构的影响
分离编译要求多平台库发布元数据 KLIB,这是 KMP Gradle 插件发布任务的默认行为,但新方案使其成为必需。库作者需确保元数据 KLIB 包含所有公共声明,否则使用分离编译的消费者可能无法解析依赖。对于应用开发者,common 代码只能调用依赖的 common 声明,这提高了跨平台代码的透明性,但可能需要重构原本隐式依赖平台声明的代码。
Q&A
Kotlin Multiplatform 的分离编译是什么?它解决了什么问题?
分离编译是 Kotlin 2.5.0-Beta1 引入的实验性编译方案,旨在解决 KMP 项目中 IDE 分析与编译器行为不一致的问题,例如 common 代码可能意外调用平台声明、重载解析和类型推断出错。启用后,编译器严格按元数据 KLIB 解析 common 代码,与 IDE 保持一致,并支持 common 源集增量编译。
如何启用 Kotlin Multiplatform 的分离编译?
在项目的 gradle.properties 文件中添加编译器选项:kotlin.kmp.separateCompilation=true。该功能是实验性的,默认禁用,启用前需注意已知问题。
启用分离编译后,common 代码中调用平台声明会怎样?
启用后,编译器会严格使用元数据 KLIB 解析 common 代码的调用,因此 common 代码中调用平台声明(如 jvmMain 中的类)将导致编译错误,而之前可能编译通过。修复方法是使用 expect/actual 声明显式连接平台声明。
分离编译如何解决重载解析不一致的问题?
在旧编译方案中,IDE 将 common 代码中的调用解析到 common 声明,而编译器可能选择更具体的平台重载,导致运行时行为与 IDE 不一致。启用分离编译后,调用仅基于 common 声明解析,因此 IDE 和编译器结果一致,运行时行为也符合预期。
分离编译能解决类型推断错误吗?如何解决?
能。例如,当平台 actual 类有额外超类型时,旧方案下编译器可能推断出错误类型导致编译失败。启用分离编译后,common 代码仅针对 common 声明解析,类型推断在 common 编译阶段完成,平台编译无需重新推断,从而避免错误。
启用分离编译有哪些已知问题或注意事项?
已知问题包括:cinterop 的 commonization 可能有问题(如 KT-88178、KT-41509),使用 C 互操作时需谨慎;具有 common 源集和单一目标的 KMP 模块暂不受影响;还有其他小问题如 KT-88148。此外,多平台库作者必须发布元数据 KLIB。