Lets say, I have a variable:
var myObject : MyObject? = null
it should be cleared in some place :
myObject?.clear
myObject = null
and should be definitely non-nullable in a place of usage. In Java I can do something like this:
private MyObject getMyObject(){
if(myObject == null) {
myObject = new MyObject()
}
return myObject
}
The question: How can I achieve that in Kotlin?
I found a suggestion to use elvis-operator:
private fun getMyObject() = myObject ?: MyObject()
but that does not assign a result (if new instance of MyObject
would be created) to the myObject
variable.
Please help me with solution and explanation. thanks ahead
The issue is that the getter and setter of a property can't have different types. I'd suggest a separate nullable private property and a method for clearing it:
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
}
If you need this pattern more than once, write a delegate.
You can use the backing field of the property.
class Foo {
var bar: String? = null
get() {
if (field == null) {
field = "Automatically set"
}
return field
}
}
To try it:
fun main() {
val foo = Foo()
foo.bar = "Manually set"
println(foo.bar)
foo.bar = null
println(foo.bar)
}
Unfortunately, the property must be nullable for this to work. You'll have to use !!
or ?.
everywhere.
You could also use a delegate. This takes more code to write the property but makes it easier to use the property elsewhere.
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
}
}
To set bar
to null
you'd call foo.clearBar()
instead of foo.bar = null
.
You can combine the Java-like approach with the usage of Elvis operator, writing something like this:
private fun getMyObject() : MyObject {
myObject = myObject ?: MyObject()
return myObject as MyObject
}
The explicit conversion as MyObject
is needed because of myObject
's declaration: var myObject MyObject? = null
. myObject
is a nullable MyObject
but getObject
's return type is MyObject
.
I hope this can help you!
You had almost found the right answer with the elvis operator, the only missing part was to assign the newly created instance back to myObject
variable:
private fun getMyObject() = myObject ?: MyObject().also { myObject = it }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With