仅当变量为空时才在 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 MyObject
。 myObject
是一个 可空 MyObject
但 getObject
的 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 }
比方说,我有一个变量:
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 MyObject
。 myObject
是一个 可空 MyObject
但 getObject
的 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 }