仅在Kotlin中实例化变量为空吗?

时间:2019-02-07 07:27:22

标签: java kotlin nullable getter backing-field

可以说,我有一个变量:

var myObject : MyObject? = null

应该在某个地方清除它:

myObject?.clear
myObject = null

,并且在使用位置绝对不能为空。在Java中,我可以执行以下操作:

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

问题:如何在Kotlin中实现这一目标?

我发现了使用Elvis-operator的建议:

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

,但这不会将结果(如果将创建MyObject的新实例)分配给myObject变量。 请帮助我解决方案和解释。谢谢你

5 个答案:

答案 0 :(得分:9)

问题在于属性的getter和setter不能具有不同的类型。我建议使用一个单独的可为空的私有属性以及一种清除它的方法:

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
}

如果您多次需要此模式,请写一个delegate

答案 1 :(得分:5)

您可以使用属性的backing field

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

答案 2 :(得分:2)

答案:

以下是@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

答案 3 :(得分:1)

您可以将类似Java的方法与Elvis运算符的用法结合使用,编写如下代码:

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

由于as MyObject的声明:myObject,因此需要进行显式转换var myObject MyObject? = nullmyObject是可为空的MyObject ,但是getObject的返回类型为MyObject。 希望对您有所帮助!

答案 4 :(得分:1)

使用elvis运算符几乎可以找到正确的答案,唯一缺少的部分是将新创建的实例分配回myObject变量:

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