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

vag*_*ran 7 kotlin

我想要一个等效于Java Optional的类,但是

  • 正确处理空值(“未设置”状态与“空设置”不同)
  • 易变
  • 使用Kotlin内置的null安全性,type参数可以为可为null或不可为null,这会影响所有方法。

非工作代码:

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())
}
Run Code Online (Sandbox Code Playgroud)

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

一些无效的建议:

  • 不要使用“ T”类型的成员?在这样的班上。如果我知道如何使它们保持未初始化状态(编译器不允许)或如何使它们具有默认初始化,就不会使用它。
  • 使用“ fun get():T?” (结果为空)。我希望结果类型具有与类类型参数相同的可空性。否则,如果在一个简单的通用类中丢失了这种null安全性,则没有任何意义,我将需要设置!! 在我确定它不能为空(编译器应确保的事情)的地方手动进行操作,使我的代码看起来像楔形编写。

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

hot*_*key 7

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

如果存储T?在属性中,则它是T与非null类型参数不同的类型,因此不允许使用TT?互换使用。

但是,进行未经检查的强制转换可让您绕过限制,并将T?值返回为T。与非null断言(!!)不同,在运行时不检查强制类型转换,并且在遇到时它不会失败null

get()如下更改功能:

fun get(): T {
    if (!isSet) {
        throw Error("Value not set")
    }
    @Suppress("unchecked_cast")
    return value as T
}
Run Code Online (Sandbox Code Playgroud)