为什么在设置值并检查它是否为空之后,智能投射失败为空值?

可以说我有以下班级:

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

这是不是线程安全的事情?

基于无效安全文档 ,我认为这将适用于1中的任何一个。该usernameprojectName被设置在相同的功能上面使用PARAMS和2,他们使用PARAMS包装在一个if语句检查他们的价值。

Kotlin编译器不能证明username或者项目username是被另一个线程同时改变的。 私人领域也没有帮助,因为反思可能绕过这一点。

相关的文件在Type Checks and Casts :

请注意,当编译器无法保证变量不能在检查和使用之间变化时,智能转换不起作用。 更具体地说,智能演员适用于以下规则:

  • val局部变量 – 总是;
  • val属性 – 如果属性是私有的或者内部的,或者在声明属性的相同模块中执行检查。 智能转换不适用于打开属性或具有自定义获取者的属性;
  • var局部变量 – 如果变量在检查和用法之间没有被修改,并且没有在修改它的lambda中被捕获;
  • var属性 – 从不(因为变量可以随时由其他代码修改)。

改为在本地变量中捕获属性引用。

它们作为params的使用被包装在一个if语句中,检查它们的值。

如果在科特林发表声明不“捕捉”财产。 当你声明一个包含一个属性的if语句并在块内再次访问它时,编译器可能会聪明地为你转换它。 但访问规则仍然相同 – getter将被调用两次。

正如@ 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 (通过助手函数显式地或隐式地),但是我相信在这里它们是不恰当的,因为它们不表达你的意图。 在某些情况下,你需要保存当前值和工作的语义,但是在这里你需要编译器 – 信任代码的语义,而且显然不一样。