Kotlin“聪明的演员是不可能的,因为这个时候财产可能已经改变了”

为什么Android Studio在使用No.2脚本时显示错误。 我发现1和2之间没有什么不同。

class Adapter { var nameList : ArrayList<String>? = null } class Program { private fun send() { val list: ArrayList<String> = ArrayList() val adapter = Adapter() // Case 1 var otherList = adapter.nameList if (otherList != null) { list.addAll(otherList) // <--- no error } // Case 2 if (adapter.nameList!=null) { list.addAll(adapter.nameList) // <--- Error here // Smart cast to 'kotlin.collections.ArrayList<String> /* = java.util.ArrayList<String> */' is impossible, because 'adapter.nameList' is a mutable property that could have been changed by this time } } } 

请解释这个情况

IDE应该给你一个警告,说明在空检查之后, adapter.nameList可能被另一个线程所改变,并且当你调用list.addAll(adapter.nameList)adapter.nameList实际上可以是null点(再次,因为不同的线程可能已经改变了这个值,这将是一个竞争条件)。

你有几个解决方案:

  1. 使nameList成为一个val ,这使得它是final的引用。 由于它是最终的,所以保证另一个线程不能改变它。 这可能不适合你的用例。

     class Adapter { val nameList : ArrayList<String>? = null } 
  2. 在做检查之前,创建名单的本地副本。 因为它是本地拷贝,所以编译器知道另一个线程不能访问它,因此它不能被改变。 在这种情况下,本地副本可以用varval来定义,但我建议使用val

     val nameList = adapter.nameList if (nameList != null) { list.addAll(nameList) } 
  3. 使用Kotlin为这种情况提供的实用程序功能之一。 let函数使用内联函数将参数调用为参数。 这意味着它有效地编译为与#2相同,但是更简洁一些。 我喜欢这个解决方案。

     adapter.nameList?.let { list.addAll(it) } 

'adapter.nameList' is a mutable property that could have been changed

这个检查和错误消息的原因是线程 。 你有什么被称为种族条件。 在很多类似的情况下,另一个线程可能会在无效性检查和list.addAll调用之间更改adapter.namelist的值。 显然这不会发生在你的情况,因为适配器没有从发送函数泄漏,但我想编译器是不够聪明,知道这一点。

相反,情况1中没有竞赛状态,因为名单只被访问一次。

如果namelistval而不是var这也不会发生 – 因为编译器知道它不能更改 – 所以它不能从非null更改为null。

你的adapter.nameList是可变属性,所以请把它转换为不可变的。

用这个

  val nameList : ArrayList<String>? = null 

而不是这个

  var nameList : ArrayList<String>? = null 

或者你也可以通过声明非空Assert来解决这个问题

  list.addAll(adapter.nameList!!) 

注意: – ! 在运行时进行评估,这只是一个操作符。

表达式(x !!)

如果x == null,则抛出KotlinNullPointerException;否则,它返回x转换为相应的非空类型(例如,在对String类型的变量调用时返回为String)。