kotlin 关键字expect/actual的作用

在 Kotlin 多平台开发(Kotlin Multiplatform, 简称 KMP)中,expect 和 actual 是一对协同工作的修饰符关键字,它们的核心作用是:解决跨平台通用代码与平台特有 API 之间的调用与实现问题。

它们构成了 Kotlin 实现"跨平台代码共享,同时保留原生平台能力"的基础机制。

一、各自的具体分工

1、expect(期望声明)

写在通用代码模块(commonMain)中。

作用:用来定义规范和接口。它告诉 Kotlin 编译器:"我在通用代码里需要使用某种功能/类/函数,但我只在这里声明它的接口和签名,具体的实现逻辑交由各个具体平台去完成。"

特点:expect 声明的方法或类不能写具体的实现代码(没有函数体)。

2、actual(具体实现)

写在特定平台模块(如 androidMain、iosMain、jvmMain、jsMain 等)中。

作用:用来提供具体实现。它告诉 Kotlin 编译器:"这是针对特定平台,对 commonMain 中那个 expect 声明的具体实现逻辑。"

特点:必须与 expect 的名称、包路径、参数类型和返回值完全匹配。

二、代码示例

  1. 声明平台特有函数(以获取设备名称为例)
    通用模块 (commonMain):

    // 声明一个期待在平台层实现的函数
    expect fun getDeviceName(): String

    // 通用代码可以直接调用这个 expect 函数
    fun printDeviceInfo() {
    println("Current device: ${getDeviceName()}")
    }

Android 模块 (androidMain):

复制代码
// 提供 Android 端的具体实现
actual fun getDeviceName(): String {
    return "Android " + android.os.Build.MODEL
}

iOS 模块 (iosMain):

复制代码
import platform.UIKit.UIDevice

// 提供 iOS 端的具体实现
actual fun getDeviceName(): String {
    return UIDevice.currentDevice.systemName + " " + UIDevice.currentDevice.systemVersion
}
  1. 利用类型别名(Typealias)直接绑定原生类
    actual 不仅可以写函数体,还可以通过 typealias 直接复用平台已有的原生类,无需重复封装:

通用模块 (commonMain):

复制代码
expect class AtomicInt(initialValue: Int) {
    fun get(): Int
    fun incrementAndGet(): Int
}

JVM / Android 模块 (jvmMain):

复制代码
// 直接将 JVM 原生的 AtomicInteger 绑定位 actual 实现
import java.util.concurrent.atomic.AtomicInteger

actual typealias AtomicInt = AtomicInteger

三、核心特点与优势

1、零运行时开销(Zero Overhead)

与传统的依赖注入(DI)或运行时条件判断(if (isAndroid))不同,expect/actual 是在编译期(Compile Time)完成链接与替换的。编译后的平台产物中完全没有多余的抽象层或反射,性能与原生代码一致。

2、类型安全

编译器会强制检查。如果某个平台(如 iosMain)忘记写对应的 actual 实现,或者参数/返回值类型与 expect 不匹配,项目在编译阶段就会直接报错,保证不会把缺失实现的代码带到运行时。

3、不仅限于函数

expect/actual 机制可以修饰:

函数(fun)

类与接口(class / interface)

属性(val / var)

枚举(enum class)

类型别名(typealias)

四、最佳实践建议

尽管 expect/actual 很强大,但在实际 KMP 项目开发中,建议不要过度使用它:

优先抽象接口(Interface):对于复杂的业务模块(如网络库、数据库、蓝牙等),建议在 commonMain 中定义标准 Kotlin interface,然后在各平台实现,通过依赖注入(如 Koin)或构造函数传递。

何时使用 expect/actual:适合用于底层基础 API(如获取系统时间、UUID 生成、平台日志打印、文件路径获取等)或绑定平台已有工具类。

相关推荐
Easyhood6 小时前
Android 开发入门实践(十四):Compose 副作用入门与四个常用 API
android·开发语言·kotlin·android jetpack·compose
松仔log16 小时前
Kotlin中级——callbackFlow
java·前端·kotlin
zh_xuan16 小时前
kotlin常见面试题
开发语言·kotlin·面试题
mmsx16 小时前
耳切法切带洞多边形:论文能跑通,真实数据为什么崩
android·kotlin
Kapaseker18 小时前
Kotlin 又要加语法糖了:这次轮到 if-else
android·kotlin
雨白2 天前
从原理到实践:使用 Kotlin KSP 仿写 ButterKnifeLite
android·kotlin
灿飞2 天前
Kotlin与Java虚拟机十一:类与属性:构造顺序、backing field 与显式 backing field
java·后端·kotlin
传奇开心果2 天前
【探秘 Jetpack Compose 框架深水区】 第8课:rememberSaveable 的边界在哪里?——Bundle、Saver 与什么值得跨进程存活
android·kotlin
Kapaseker2 天前
领导问我 Kotlin 的 Nothing 是什么?
android·kotlin