5

可以说,我有一个变量:

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

4

5 回答 5

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
}

如果您多次需要此模式,请编写委托

于 2019-02-07T09:40:32.403 回答
5

您可以使用属性的支持字段

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
    }

}

要设置barnull您会调用foo.clearBar()而不是foo.bar = null.

于 2019-02-07T08:49:04.460 回答
2

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

private fun getMyObject() = myObject ?: MyObject().also { myObject = it }
于 2019-02-08T01:09:28.407 回答
2

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

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

由于's 声明:as MyObject需要显式转换。是可空的,但的返回类型是。我希望这可以帮助你!myObjectvar myObject MyObject? = nullmyObjectMyObjectgetObjectMyObject

于 2019-02-07T07:51:46.863 回答
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.

于 2019-02-07T09:17:40.340 回答