最常见的原因是expect写在commonMain而actual未置于对应平台源集(如androidMain、iosX64Main等),或Gradle未启用该平台目标,或函数签名(含suspend、泛型、可空性)不严格一致。

expect 声明和 actual 实现不在同一源集里
最常见的原因是 expect 写在 commonMain,但对应的 actual 没有放在正确的平台源集下。KMP 要求每个 actual 必须严格落在对应目标平台的源集里:Android 项目得在 androidMain,iOS 得在 iosX64Main 或 iosArm64Main(取决于架构),JS 得在 jsMain,WASM 得在 wasmJsMain。
检查方式很简单:打开项目结构,确认 src/androidMain/kotlin 下有没有和 commonMain 中 expect 函数签名完全一致的 actual 声明;同理核对其他平台源集。漏掉任一平台,编译时就会报 Unresolved reference: xxx 或 Actual declaration is not found。
-
commonMain里只能有expect,不能有actual -
androidMain里只能有actual,且必须与commonMain的expect在同一 package、同名、参数/返回值类型严格一致(包括可空性) - 如果用了多目标 iOS(如同时支持模拟器和真机),
iosX64Main和iosArm64Main都得各自提供actual,不能只写一个
函数签名不完全一致,尤其是泛型或 suspend 修饰符
KMP 对 expect/actual 的匹配是字节码级严格的。哪怕只是少了个 suspend、泛型约束写法不同(T : Any vs T)、或参数默认值位置不一致,都会导致匹配失败,错误提示往往很模糊,比如 Expected and actual declarations have different signatures。
举个典型例子:
// commonMain expect fun getCurrentTime(): Long // androidMain —— 错误:加了 suspend,签名已变 actual suspend fun getCurrentTime(): Long = System.currentTimeMillis() // 正确:去掉 suspend,保持签名一致 actual fun getCurrentTime(): Long = System.currentTimeMillis()
- 所有
actual必须和expect的函数名、参数名(Kotlin 1.9+ 支持命名参数匹配)、参数类型、返回类型、是否suspend、是否inline完全一致 - 泛型参数不能加额外约束,例如
expect fun,parse(json: String): T actual不能写成 - 默认参数值可以不同,但参数列表结构(数量、顺序、可空性)必须相同
Gradle 配置没启用对应平台目标
即使你写了 iosX64Main 下的 actual,如果 build.gradle.kts 里根本没声明 iosX64() 目标,Gradle 就不会把那个源集纳入编译路径——结果就是 IDE 看得见、编译器却“视而不见”,依然报匹配失败。
检查 kotlin { } 块里的 targets 是否和你写的 actual 平台一一对应:
kotlin {
jvm()
iosX64() // ← 如果你写了 iosX64Main,这行必须存在
iosArm64() // ← 同理,真机目标也得显式启用
js().browser()
}
- 新增平台目标后,务必重新 sync Gradle,否则 IDE 不会识别新源集
- 某些旧版 Kotlin 插件对 WASM 支持不稳定,若用
wasmJs(),需确认 Kotlin 版本 ≥ 1.9.20 - Android 目标要确保
androidTarget()已配置,且androidMain源集被正确识别(不是main或androidTest)
IDE 缓存或 Kotlin 插件版本错位
Android Studio 或 IntelliJ 有时会缓存旧的解析结果,表现为:明明改对了,IDE 还标红;或者改完之后 clean + rebuild 仍报错。更隐蔽的是,Kotlin 插件版本和项目中实际使用的 Kotlin 编译器版本不一致,导致 expect/actual 解析逻辑本身出错。
优先排查这两点:
- 执行
File → Invalidate Caches and Restart… → Invalidate and Restart,不要勾选 “Remove repository caches”(除非你确定需要) - 确认
gradle.properties中kotlin.version和build.gradle.kts里plugins { kotlin("multiplatform") version "x.y.z" }的版本号完全一致 - 检查
commonMain的expect是否被意外 import 了某个平台专属类(比如android.os.Build),这会导致它被 IDE 当作平台相关代码,拒绝出现在commonMain
expect/actual 匹配失败很少是语法问题,绝大多数时候是工程配置层面的“错位”——平台没启用、源集放错位置、签名差一个问号。盯住这三个地方,比反复重写函数更有效。











