仅当变量为空时才在 Kotlin 中实例化变量?

Instantiate a variable in Kotlin only if it is a null?

比方说,我有一个变量:

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 变量。 请帮我解决和解释。先谢谢了

您可以将类似 Java 的方法与 Elvis 运算符的用法结合起来,编写如下内容:

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

由于 myObject 的声明:var myObject MyObject? = null,需要显式转换 as MyObjectmyObject 是一个 可空 MyObjectgetObject 的 return 类型是 MyObject。 希望对您有所帮助!

您可以使用 属性 的 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

答案:

@Slaw 版本的较短版本如下:

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

如果 属性 在 get 访问中 null,这将为支持字段实例化一个新的 MyObject


附加信息:

您可能会想到类似于以下内容的较短版本

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

请记住,这不会编译,并显示错误消息:Assignments are not expressions, and only expressions are allowed in this context

问题是 属性 的 getter 和 setter 不能有不同的类型。我建议一个单独的可为 null 的私有 属性 和一个清除它的方法:

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.

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

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