【发布时间】:2018-10-07 01:56:54
【问题描述】:
我想要一个与 Java Optional 等价的类
- 正确处理空值(“未设置”状态与“空设置”不同)
- 是可变的
- 使用 Kotlin 内置的 null 安全性,类型参数可以为 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())
}
问题是,如果我尝试设置 null,get() 调用会失败并出现空指针异常(由 !! 运算符引起)。
一些无效的建议:
- 不要使用“T?”类型的成员?在这样的课程中。如果我知道如何使它们保持未初始化(编译器不允许)或如何使它们具有默认初始化,我不会使用它。
- 使用“fun get(): T?” (结果可为空)。我希望结果类型与类类型参数具有相同的可空性。否则,如果它在一个简单的泛型类中丢失,那么这种空安全性就没有任何意义,我需要设置 !!在我确定它不可为空的地方手动手动操作(编译器应该确保的事情),使我的代码看起来像楔形书写。
注意:这个例子是合成的,我不需要 mutable 可选,它只是一个简单易懂的例子,说明了我偶尔遇到的 Kotlin 泛型和空安全问题。找到这个特定示例的解决方案将有助于解决许多类似的问题。实际上我有这个类的不可变版本的解决方案,但它涉及为当前和非当前值制作接口和两个实现类。这种不可变的可选项可以用作“值”成员的类型,但我认为这是相当大的开销(会计还为每个 set() 创建包装器对象)只是为了克服语言限制。
【问题讨论】:
-
更好的做法:you 不应处理 null,但只有在泛型类型为 null 时才应处理 null。也就是说,
value应该具有T类型,但是如果用户想要允许可空的Foos,则可以创建MutableOptional<Foo?>。 -
是的,这正是问题中描述的 MutableOptional 类的要求。并且还提到了使
value类型为T的问题-您无法将其初始化为某些东西。但是,现在我看到它可以初始化为null as T,这是实现的第二个变体,其中包含接受的答案。
标签: kotlin