如何在Kotlin中实现可变选项?

时间:2018-04-26 11:26:14

标签: kotlin

我想要一个等同于Java Optional但又

的类
  • 正确处理空值("未设置"状态不同于"空集")
  • 是可变的
  • 使用Kotlin内置的null-safe,类型参数可以是可空的或不可为空的,这会影响所有方法。

非工作代码:

class MutableOptional<T> {
    private var value: T? = null
    private var isSet: Boolean = false

    fun set(value: T)
    {
        this.value = value
        isSet = true
    }

    fun unset()
    {
        isSet = false
        value = null
    }

    fun get(): T
    {
        if (!isSet) {
            throw Error("Value not set")
        }
        return value!! // <<< NPE here
    }
}

fun f()
{
    val opt = MutableOptional<Int?>()
    opt.set(null)
    assertNull(opt.get())
}

问题在于,如果我尝试设置null,则get()调用将失败并返回空指针异常(由!!运算符引起)。

一些不起作用的提案:

  • 不要使用&#34; T?&#34;类型的成员在这样的课程。如果我知道如何让它们保持未初始化(编译器不允许)或者如何让它们进行默认初始化,我就不会使用它。
  • 使用&#34;有趣的get():T?&#34; (可以为空的结果)。我希望结果类型具有与类类型参数相同的可为空性。如果它在一个简单的泛型类中丢失,那么这种null安全没有意义,我需要设置!!手动我确定它是不可为空的(编译器应该确保的东西),使我的代码看起来像楔形写。

注意:这个例子是合成的,我真的不需要mutable可选,它只是一个简单易懂的例子,说明了偶尔遇到的Kotlin泛型和null安全问题。找到这个特定示例的解决方案将有助于解决许多类似的问题。实际上我有这个类的不可变版本的解决方案,但它涉及为当前和非现在值创建接口和两个实现类。这种不可变的可选项可以用作&#34;值&#34;的类型。成员,但我认为这是一个相当大的开销(也考虑到每个set()的包装对象创建)只是为了克服语言限制。

2 个答案:

答案 0 :(得分:6)

编译器希望您编写对所有可能的T都是类型安全的代码,可以是nullable和not-null(除非您为type参数指定了非null的上限,例如{{ 1}},但这不是你需要的。)

如果将T : Any存储在属性中,则在非空类型参数的情况下,它与T?的类型不同,因此不允许使用T和{{ 1}}可互换。

但是,制作unchecked cast可以绕过限制并将T值作为T?返回。与非空断言(T?)不同,演员在运行时不会被检查,并且在遇到T时不会失败。

更改!!功能,如下所示:

null

答案 1 :(得分:0)

我遇到了类似的问题。我的用例是在反序列化JSON对象时区分null和undefined值。因此,我创建了一个能够处理空值的不可变Optional。在这里,我分享我的解决方案:

interface Optional<out T> {
    fun isDefined(): Boolean
    fun isUndefined(): Boolean
    fun get(): T
    fun ifDefined(consumer: (T) -> Unit)

    class Defined<out T>(private val value: T) : Optional<T> {
        override fun isDefined() = true
        override fun isUndefined() = false
        override fun get() = this.value
        override fun ifDefined(consumer: (T) -> Unit) = consumer(this.value)
    }

    object Undefined : Optional<Nothing> {
        override fun isDefined() = false
        override fun isUndefined() = true
        override fun get() = throw NoSuchElementException("No value defined")
        override fun ifDefined(consumer: (Nothing) -> Unit) {}
    }
}

fun <T> Optional<T>.orElse(other: T): T = if (this.isDefined()) this.get() else other

诀窍:必须将orElse方法定义为不破坏协方差的扩展,因为Kotlin does not support lower bound for now

然后,我们可以通过以下方式定义不强制转换的MutableOptional:

class MutableOptional<T> {

    private var value: Optional<T> = Optional.Undefined

    fun get() = value.get()
    fun set(value: T) { this.value = Optional.Defined(value) }
    fun unset() { this.value = Optional.Undefined }
}

我对我一成不变的Optional实现感到满意。但是我对MutableOptional不太满意:我不喜欢以前基于投射的解决方案(我不喜欢投射)。但是我的解决方案会产生不必要的装箱,这可能更糟...