可选的 Kotlin 多平台支持

Kotlin multiplatform support for Optional

我正在使用 Java API 现在转换为 multiplatform Kotlin。它曾经使用 java.lang.Optional 作为许多调用的 return 类型。我知道这是 (see discussion) 但这是一个现有的 API,可选的保留(而且它不是 不好的 选择 Java-面向客户)。我的问题是 如何?

注:代码只需要return Optional.of(x)return Optional.empty()到外部API。任何内部使用都将被清除。

此时,Kotlin 不允许使用 Java class 为预期的 class 和 companion object 提供 actual typealias匹配 static 声明。关注本期更新:KT-29882.

目前,您可以通过在预期 Optional class 之外单独声明工厂函数来解决此问题,如下所示:

expect class Optional<T : Any> {
    fun get(): T
    fun isPresent(): Boolean
    /* ... */
}

expect object Optionals {
    fun <T : Any> of(t: T): Optional<T>
    fun empty(): Optional<Nothing>
}

不一定是object,你可以只使用顶级函数。

然后,在 JVM 上,您必须为 Optional class 提供 actual typealias,另外,为 Optionals 提供简单的实际实现对象:

actual typealias Optional<T> = java.util.Optional<T>

actual object Optionals {
    actual fun <T : Any> of(t: T): Optional<T> = java.util.Optional.of(t)
    actual fun empty(): Optional<Nothing> = java.util.Optional.empty()
}

至于不为非 JVM 平台提供实现,我怀疑这是可能的,因为这将需要对 Optional 用法进行一些重要的编译时转换,使其仅可空类型。所以你会想要这样的东西:

actual typealias Optional<T> = T?

现在是一个错误:

Type alias expands to T?, which is not a class, an interface, or an object

所以你实际上需要一个非 JVM 实现。为避免为每个非 JVM 目标重复它,您可以声明一个自定义源集并 link 它与特定于平台的源集一起,以便他们从那里获得实现:

build.gradle.kts

kotlin {
    /* targets declarations omitted */

    sourceSets {
        /* ... */

        val nonJvmOptional by creating {
            dependsOn(getByName("commonMain"))
        }
        configure(listOf(js(), linuxX64())) { // these are my two non-JVM targets
            compilations["main"].defaultSourceSet.dependsOn(nonJvmOptional)
        }
    }
}

然后,在此自定义源集内(例如 src/nonJvmOptional/kotlin/OptionalImpl.kt),您可以为非 JVM 目标提供实际实现。

这是 Github 上的一个最小项目示例,我在其中试验了上述内容:h0tk3y/mpp-optional-demo