我已经读过了!!一般应避免。有没有一种方法可以以更优雅的方式编写以下代码,而不必添加过时的空检查、重复或死代码块之类的内容?
class A(var field: Thing?) {
fun getField(): Thing {
if (field == null) {
field = Thing()
}
return field!!
}
}
此外,我不明白编译器为什么需要!!-'祈祷在这种情况下,这不是一个完全的操作员。
编辑:考虑一下,如果字段为null,那么潜在的解决方案使用延迟初始化对我来说很重要!
问题
正如Enzokie在评论中已经提到的那样,在null检查之后,另一个线程可能已经更改了字段。编译器无法知道这一点,所以你必须告诉它
class A(var field: Thing?) {
fun getField(): Thing {
if (field == null) {
field = Thing()
}
// another thread could have assigned null to field
return field!! // tell the compiler: I am sure that did not happen
}
}
解决方案(渴望)
在您的特定情况下,最好在构造函数中使用参数f
(您也可以将其命名为"字段",但为了清楚起见,我避免了这样做)(没有val
/var
),然后将其分配给属性field
,您可以将f
或Thing
的新实例分配给该属性。
这可以用Elvis算子:?
非常简洁地表达,如果不为空,则取表达式的左手边,否则取表达式的右手边。因此,在结束字段中的类型将是Thing
。
class A(f: Thing?) {
val field = f ?: Thing() // inferred type Thing
}
解决方案(懒惰)
由于gids已经提到了这一点,如果您需要懒散地初始化字段,您可以使用委托的属性来这样做:
class A(f: Thing?) {
val field by lazy {
f ?: Thing() // inferred type Thing
}
}
呼叫站点不变:
val a = A(null) // field won't be initialized after this line...
a.field // ... but after this
这个怎么样?
class A(field: Thing?) {
private lateinit var field: Thing
init {
field?.let { this.field = it }
}
fun getField(): Thing {
if (!this::field.isInitialized) {
field = Thing()
}
return field
}
}
定义字段时,实际上定义了一个变量和两个访问器方法:
val counter: Integer = 0
可以通过编写以下内容来自定义访问器方法:
val n = 0
val counter: Integer
get() = n++
这将在每次访问counter
字段时执行n++
,因此每次访问都会返回不同的值。这并不常见,也出乎意料,但在技术上是可能的。
因此,Kotlin编译器不能假设对同一字段的两次访问会两次返回相同的值。通常他们会这样做,但这并不能保证。
为了解决这个问题,您可以通过将字段复制到本地变量中来读取一次:
fun count() {
val counter = counter
println("The counter is $counter, and it is still $counter.")
}