Kotlin“聪明的演员阵容是不可能的,因为此时房产可能已被改变”

时间:2017-10-12 03:42:25

标签: android 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
        }
    }
}

请解释此案例

3 个答案:

答案 0 :(得分:20)

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

您有几个解决方案:

  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) }
    

答案 1 :(得分:4)

您的adapter.nameList是可变属性,因此请将其转换为immutable。

使用此

  val nameList : ArrayList<String>? = null

而不是这个

  var nameList : ArrayList<String>? = null

或者你也可以通过断言非null Assert来解决这个问题

            list.addAll(adapter.nameList!!)

注意: - !!在运行时进行评估,它只是一个运算符。

表达式(x !!)

如果x == null,

抛出KotlinNullPointerException, 否则,它将x cast返回到相应的非可空类型(例如,当在类型为String的变量上调用时,它会将其作为String返回。)。

答案 2 :(得分:1)

  

adapter.nameList是一个可以更改的可变属性

此检查和错误消息的原因是线程。你所拥有的是一种竞争条件。在许多类似的情况下,另一个线程可能会在无效性检查和adapter.namelist调用之间更改list.addAll的值。显然,在你的情况下不会发生这种情况,因为适配器没有从发送功能中泄露,但我想编译器并不够聪明,不知道。

相比之下,案例1中没有竞争条件,因为名单只能访问一次。

如果namelistval而不是var,则不会发生这种情况 - 因为编译器知道它不能改变 - 所以它不能从非null变为null。