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 生成、平台日志打印、文件路径获取等)或绑定平台已有工具类。

相关推荐
Android打工仔2 小时前
不要再把 Flow 当作一个容器
android·kotlin
天空之城--14 小时前
Android行业一周动态:编码趋势与行业资讯汇总
android·性能优化·架构·kotlin·android jetpack
JMchen1 天前
第 2 篇|Kotlin 进阶 —— 集合、循环与条件表达式
android·kotlin
YB13751 天前
Kotlin 三类关键字
kotlin
撩得Android一次心动2 天前
Jetpack Compose 知识点整理1【个人用】
android·学习·kotlin·android jetpack·compose
ForteScarlet2 天前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
JMchen1232 天前
Cursor高阶玩法:.cursorrules编写与Spec-Driven开发实战,把AI调教成专属结对编程搭档
人工智能·vscode·kotlin·cursor·结对编程·spec-driven·.cursorrules
hai_android2 天前
Kotlin 协程作用域源码剖析
android·kotlin
leluckys2 天前
【KMP】-KMP 项目commonMain、androidMain、iosMain 到底放什么?
kotlin