假设我有以下课程:
class MyClass {
private var username: String? = null
private var projectName: String? = null
private var buildNumber: Int = -1
private val presenter: Presenter = Presenter()
fun present() {
username = ""
projectName = ""
if (username != null && projectName != null && buildNumber != -1) {
presenter.viewReady(this, username, projectName, buildNumber)
} else {
throw Exception("You did something bad!")
}
}
}
为什么我收到错误Smart cast to 'String' is impossible, because 'username' is a mutable property that could have been changed by this time
?
是否与线程安全无关?
基于null safety docs,我认为这适用于1. username
和projectName
设置在相同的函数中,而不是它们用作params和2.它们作为params的用法包含在if语句中,检查它们的值。
答案 0 :(得分:7)
Kotlin编译器无法证明username
或projectName
同时被另一个线程变异。私有的领域也没有帮助,因为反思可能绕过这个。
相关文档位于Type Checks and Casts:
下请注意,当编译器无法保证变量在检查和使用之间无法更改时,智能强制转换不起作用。更具体地说,智能演员表适用于以下规则:
- val局部变量 - 总是;
- val属性 - 如果属性为private或internal,或者在声明属性的同一模块中执行检查。智能转换不适用于具有自定义getter的打开属性或属性;
- var局部变量 - 如果在检查和使用之间没有修改变量,并且没有在修改它的lambda中捕获;
- var properties - never(因为其他代码可以随时修改变量)。
改为在局部变量中捕获属性引用。
将它们用作params包含在if语句中检查它们的值。
如果Kotlin中的陈述没有“捕获”一个财产。当您声明包含属性的if语句并在块内再次访问它时,编译器可能会为您强制转换它。但是访问规则仍然相同 - getter将被调用两次。
答案 1 :(得分:1)
正如@ mEQ5aNLrK3lqs3kfSa5HbvsTWe0nIu指出的那样,Kotlin不会激活var
属性的智能投射。我不确定为什么选择这种策略,因为private var
可以在预发布版本中智能化,但这就是我们所拥有的。
现在,假设您确定这些属性不会被另一个线程变异,因为它们是private
并且没有使用反射。因此if-null
检查确保属性不包含空值,但Kotlin编译器不相信。
在这种情况下,我强烈建议使用not-null-assertion运算符!!
:
presenter.viewReady(this, username!!, projectName!!, buildNumber)
许多人建议避免!!
改进您的代码风格,但其含义实际上是"the compiler is dumb, it is obviously not a null, notify me if I am wrong"
。像你这样的情况是它首先被引入语言的原因。
还有其他解决方法,包括将属性的当前值保存到本地val
(通过帮助函数显式或隐式),但我认为它们在这里不合适,因为它们不表达您的意图。在某些情况下,你需要使用save-current-value-and-work-with-it语义,但是你需要make-the-compiler-trust-the-code语义,它们显然是不一样的。