Kotlin多平台支持Optional

3
我现在正在使用一个Java API,已转换为多平台Kotlin。它以前使用java.lang.Optional作为许多调用的返回类型。我知道这不是Kotlin惯用方式(参见not the idiomatic Kotlin-way (请参见discussion),但这是一个现有的API,Optional仍然存在(对于面向Java客户端而言,这也不是一个坏选择)。我的问题是如何处理? 注意:代码只需要返回Optional.of(x)或Optional.empty()到外部API。任何内部使用都将被清除。
  • 我们如何使用 expect/actual/typealias 以便在可用时使用真正的 Optional 类?
  • 是否有一种方法可以避免在非 Java 目标上重新实现一个“伪” Optional 类(即与可空的?后缀习惯地工作)?
1个回答

2
目前,Kotlin不支持使用具有匹配的static声明的Java类为带有companion object的期望类提供actual typealias。请关注此问题以获取更新: KT-29882
目前,您可以通过在期望的Optional类之外单独声明工厂函数来解决此问题,如下所示:
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>
}

这不一定需要是一个对象,你可以只使用顶级函数。

然后,在JVM上,您需要为Optional类提供一个实际的类型别名,并且另外还要提供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?

现在已经变成了一个错误:

类型别名扩展为T?,它不是一个类、接口或对象

因此,您实际上需要一个非JVM实现。为了避免为每个非JVM目标重复此操作,您可以声明一个自定义源集并将其链接到特定于平台的源集,以便它们从那里获取实现:

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


如果 actual typealias Optional<T> = T? 能够正常工作,那就太棒了。 - drekbour
@drekbour,据我所知,这几乎是这种类型别名唯一可行的用例,而且它需要非平凡的规则,这就是为什么它被禁止的原因。 - hotkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接