Создавать переменную в 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
,