Создавать переменную в Kotlin, только если она нулевая?

Допустим, у меня есть переменная:

var myObject : MyObject? = null

это должно быть очищено в каком-то месте:

myObject?.clear
myObject = null

и должен быть определенно не обнуляемым в месте использования. В Java я могу сделать что-то вроде этого:

private MyObject getMyObject(){
  if(myObject == null) {
    myObject = new MyObject()
  }
  return myObject
}

Вопрос: как мне этого добиться в Котлине?

Я нашел предложение использовать elvis-operator:

private fun getMyObject() = myObject ?: MyObject()

но это не присваивает результат (если новый экземпляр MyObject будет создан) к myObject переменная. Пожалуйста, помогите мне с решением и объяснением. спасибо вперед

5 ответов

Решение

Проблема в том, что метод получения и установки свойства не может иметь разные типы. Я бы предложил отдельную обнуляемую частную собственность и метод ее очистки:

private var _myObject: MyObject? = null

var myObject: MyObject // or val, depending
    get() {
        if (_myObject == null) { _myObject = MyObject() }
        return _myObject!!
    }
    set(value: MyObject) { 
        _myObject?.clear()
        _myObject = value
    }

fun clearMyObject() {
    _myObject?.clear()
    _myObject = null
}

Если вам нужен этот шаблон более одного раза, напишите делегат.

Вы можете использовать поле поддержки свойства.

class Foo {

    var bar: String? = null
        get() {
            if (field == null) {
                field = "Automatically set"
            }
            return field
        }


}

Чтобы попробовать это:

fun main() {
    val foo = Foo()
    foo.bar = "Manually set"
    println(foo.bar)
    foo.bar = null
    println(foo.bar)
}

К сожалению, свойство должно быть обнуляемым, чтобы это работало. Вам придется использовать !! или же ?. везде.


Вы также можете использовать делегата. Для написания свойства требуется больше кода, но легче использовать свойство в другом месте.

class Foo(initBar: String? = null) {

    private val barDelegate = NullDelegate(initBar) { "Automatically set" }
    var bar: String by barDelegate // not nullable to outside world

    fun clearBar() {
        barDelegate.clear()
    }

}

// Reusable. Not thread-safe.
class NullDelegate<T>(
    private var value: T? = null, 
    private val supplier: () -> T
) {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        if (value == null) value = supplier()
        return value!!
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        this.value = value
    }

    fun clear() {
        value = null
    }

}

Установить bar в null ты бы позвонил foo.clearBar() вместо foo.bar = null,

Вы можете объединить Java-подобный подход с использованием оператора Элвиса, написав что-то вроде этого:

private fun getMyObject() : MyObject {
    myObject = myObject ?: MyObject()
    return myObject as MyObject
}

Явное преобразование as MyObject необходим из-за myObjectобъявление: var myObject MyObject? = null, myObject обнуляемыйMyObject но getObjectвозвращаемый тип MyObject, Я надеюсь это тебе поможет!

Вы почти нашли правильный ответ с оператором elvis, единственной недостающей частью было назначить вновь созданный экземпляр обратно myObject переменная:

private fun getMyObject() = myObject ?: MyObject().also { myObject = it }

Ответ:

Укороченная версия версии @Slaw:

var myObject: MyObject? = null
    get() {
        field = field ?: MyObject()
        return field
    }

Это будет создавать новую MyObject к вспомогательному полю, если свойство null на get доступ.


Дополнительная информация:

Вы могли бы подумать о более короткой версии, похожей на следующую

var myObject: Any? = null
    get() = field = field ?: Any() // Does not compile

Пожалуйста, имейте в виду, что это не компилируется с сообщением об ошибке: Assignments are not expressions, and only expressions are allowed in this context,

Другие вопросы по тегам