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点(再次,因为不同的线程可能已经改变了这个值,这将是一个竞争条件)。
你有几个解决方案:
-
使
nameList
成为一个val
,这使得它是final
的引用。 由于它是最终的,所以保证另一个线程不能改变它。 这可能不适合你的用例。class Adapter { val nameList : ArrayList<String>? = null }
-
在做检查之前,创建名单的本地副本。 因为它是本地拷贝,所以编译器知道另一个线程不能访问它,因此它不能被改变。 在这种情况下,本地副本可以用
var
或val
来定义,但我建议使用val
。val nameList = adapter.nameList if (nameList != null) { list.addAll(nameList) }
-
使用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中没有竞赛状态,因为名单只被访问一次。
如果namelist
是val
而不是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)。