Kotlin expect 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 多平台项目使用 expect
和 actual
关键字来定义平台特定的实现。 expect
关键字声明一个必须为每个目标平台实现的 API。
基本定义
expect
关键字标记一个声明,该声明预期具有平台特定的实现。 它与平台模块中的 actual
声明配对。 这种机制支持共享公共代码,同时允许平台差异。
基本的 expect/actual 函数
最简单的用例是声明一个每个平台都必须实现的预期函数。 公共模块定义了预期的签名和返回类型。
package com.zetcode.common expect fun getPlatformName(): String
package com.zetcode.jvm actual fun getPlatformName(): String = "JVM"
公共模块声明了一个预期的函数 getPlatformName
。 JVM 模块提供了实际的实现。 其他平台将有自己的实现。 编译器确保所有预期的声明都有实际的实现。
expect/actual 类
类也可以使用 expect
声明,并在每个平台上以不同的方式实现。 公共模块定义了预期的接口,而平台模块提供实现。
package com.zetcode.common expect class PlatformTimer { fun start() fun stop() fun getCurrentTime(): Long }
package com.zetcode.jvm import java.time.Instant actual class PlatformTimer { private var running = false private var startTime: Instant? = null actual fun start() { running = true startTime = Instant.now() } actual fun stop() { running = false } actual fun getCurrentTime(): Long { return Instant.now().toEpochMilli() - (startTime?.toEpochMilli() ?: 0) } }
公共模块声明了一个预期的 PlatformTimer
类,它有三个方法。 JVM 实现使用 Java 的 Instant
类。 其他平台将使用其本地计时机制,同时保持相同的接口。
expect/actual 属性
属性可以使用 expect
声明,并在每个平台上以不同的方式实现。 这对于特定于平台的常量或值很有用。
package com.zetcode.common expect val platformLineSeparator: String
package com.zetcode.jvm actual val platformLineSeparator: String = System.lineSeparator()
package com.zetcode.native actual val platformLineSeparator: String = "\n"
公共模块期望一个换行符属性。 JVM 实现使用 System.lineSeparator
,而本地实现使用一个简单的换行符。 这允许特定于平台的行为,同时保持一致的 API。
expect/actual 对象
单例对象可以使用 expect
声明,并在每个平台上以不同的方式实现。 这对于特定于平台的服务很有用。
package com.zetcode.common expect object PlatformService { fun initialize() fun getVersion(): String fun shutdown() }
package com.zetcode.jvm actual object PlatformService { private var initialized = false actual fun initialize() { // JVM-specific initialization initialized = true } actual fun getVersion(): String { return System.getProperty("java.version") } actual fun shutdown() { // JVM-specific cleanup initialized = false } }
公共模块声明了一个预期的单例对象,它有三个方法。 JVM 实现使用 Java 系统属性并维护初始化状态。 其他平台将提供自己的实现,同时保持相同的接口。
expect/actual 与默认实现
预期的声明可以提供一个默认实现,平台可以选择性地覆盖它。 这减少了平台共享行为时的样板代码。
package com.zetcode.common expect class FileHandler { fun readFile(path: String): String fun writeFile(path: String, content: String) fun fileExists(path: String): Boolean { // Default implementation return try { readFile(path) true } catch (e: Exception) { false } } }
package com.zetcode.jvm import java.io.File actual class FileHandler { actual fun readFile(path: String): String { return File(path).readText() } actual fun writeFile(path: String, content: String) { File(path).writeText(content) } // Inherits default fileExists implementation }
公共模块声明 FileHandler
,它有两个抽象方法和一个默认实现。 JVM 模块使用 Java 的 File
类实现抽象方法,并继承默认的 fileExists
实现。 平台可以在需要时覆盖默认值。
expect/actual 枚举类
枚举类可以使用 expect
声明,并在每个平台上以不同的方式实现。 这允许特定于平台的枚举条目或行为。
package com.zetcode.common expect enum class PlatformType { DESKTOP, MOBILE, WEB, EMBEDDED; abstract fun getDisplayName(): String }
package com.zetcode.jvm actual enum class PlatformType { DESKTOP { override fun getDisplayName() = "Java Desktop" }, MOBILE { override fun getDisplayName() = "Android" }, WEB { override fun getDisplayName() = "JVM Web" }, EMBEDDED { override fun getDisplayName() = "JVM Embedded" }; actual override fun getDisplayName(): String }
公共模块声明了一个预期的枚举,它有一个抽象方法。 JVM 实现为每个枚举条目提供特定于平台的显示名称。 其他平台将实现自己的版本,并提供适当的显示名称。
expect/actual 接口
接口可以使用 expect
声明,并在每个平台上以不同的方式实现。 这对于通用接口后面的特定于平台的功能很有用。
package com.zetcode.common expect interface PlatformStorage { fun saveData(key: String, value: String) fun loadData(key: String): String? fun clearData(key: String) }
package com.zetcode.jvm import java.util.prefs.Preferences actual class PlatformStorageImpl : PlatformStorage { private val prefs = Preferences.userRoot() actual override fun saveData(key: String, value: String) { prefs.put(key, value) } actual override fun loadData(key: String): String? { return prefs.get(key, null) } actual override fun clearData(key: String) { prefs.remove(key) } }
公共模块声明了一个预期的存储接口。 JVM 实现使用 Java 的 Preferences
API。 其他平台将使用其本地存储机制,同时保持相同的接口约定。
expect/actual 的最佳实践
- 最小化特定于平台代码: 将大多数代码保留在公共模块中,仅将 expect/actual 用于基本差异。
- 维护一致的 API: 确保所有实际实现都遵循与其 expect 声明相同的约定。
- 使用默认实现: 尽可能提供默认值以减少平台实现工作量。
- 记录平台差异: 清楚地记录平台实现之间的任何行为差异。
- 测试每个平台: 验证所有实际实现在其目标平台上是否正确运行。
来源
本教程深入探讨了 Kotlin 的 expect
关键字,展示了它如何实现多平台开发。 我们探讨了各种声明类型及其特定于平台的实现。 适当使用 expect/actual 允许共享代码,同时干净地处理平台差异。
作者
列出 所有 Kotlin 教程。