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 教程。