Я хочу класс, который эквивалентен Java Optional, но также
- Правильно обрабатывает нулевое значение (состояние «Не установлено» отличается от «Нулевого набора»)
- Изменчиво
- Использует встроенную в Kotlin нулевую безопасность, параметр типа может быть как обнуляемым, так и ненулевым, что влияет на все методы.
нерабочий код:
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?" (с обнуляемым результатом) . Я хочу, чтобы тип результата имел такую же обнуляемость, что и параметр типа класса. Иначе в такой нулевой безопасности нет смысла, если она потеряна в простом обобщенном классе, и мне нужно будет установить !! вручную, где я уверен, что он не обнуляем (что должен гарантировать компилятор), что делает мой код похожим на клиновую запись.
Примечание: этот пример является синтетическим, мне не нужен изменяемый необязательный параметр, это просто простой и понятный пример, иллюстрирующий проблему, с которой я иногда сталкиваюсь, с обобщениями Kotlin и нулевой безопасностью. Нахождение решения этого конкретного примера поможет со многими подобными проблемами. На самом деле у меня есть решение для неизменной версии этого класса, но оно включает создание интерфейса и двух классов реализации для существующих и отсутствующих значений. Такой неизменный необязательный параметр можно использовать как тип элемента «value», но я думаю, что это довольно большие издержки (учитывая также создание объекта-оболочки для каждого set ()) просто для преодоления языковых ограничений.