当前位置: 首页 > 知识库问答 >
问题:

Kotlin“智能转换是不可能的,因为此时属性可能已更改”

宋经业
2023-03-14

为什么当我使用No.2脚本时Android Studio显示错误。我发现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个答案

邓欣可
2023-03-14

< code>adapter.nameList是一个可能已被更改的可变属性

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

相反,在案例1中没有竞争条件,因为名称列表只被访问一次。

如果名称列表val,而不是var-这也不会发生,因为编译器知道它不能更改-因此它不能从非null更改为null。

那昊
2023-03-14

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

使用这个

  val nameList : ArrayList<String>? = null

代替此

  var nameList : ArrayList<String>? = null

或者,您也可以通过断言非空 Assert 来解决此问题

            list.addAll(adapter.nameList!!)

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

表达式(x!)

如果 x == null,则抛出一个 KotlinNullPointerException,否则,它将 x 强制转换为相应的不可为 null 的类型(例如,当对 String? 类型的变量调用时,它会将其作为 String 返回)。

邵骁
2023-03-14

IDE会向您发出警告,说明在进行空值检查后,< code>adapter.nameList可能已被另一个线程更改,并且当您调用< code > list . addall(adapter . name list)时,< code>adapter.nameList实际上可能已为空值(同样,因为另一个线程可能已更改了该值)。这将是一种竞争条件)。

您有一些解决方案:

>

  • nameList 设置为 val,这将使其引用成为最终值。由于它是最终的,因此可以保证另一个线程无法更改它。这可能不适合您的使用案例。

    class Adapter {
        val nameList : ArrayList<String>? = null
    }
    

    执行检查之前,请创建名称列表的本地副本。因为它是本地副本,所以编译器知道另一个线程无法访问它,因此无法更改它。在这种情况下,本地副本可以用 varval 来定义,但我建议使用 val

    val nameList = adapter.nameList
    if (nameList != null) {
        list.addAll(nameList)
    }
    

    使用Kotlin为这种情况提供的实用程序函数之一。let函数使用内联函数复制作为参数调用的引用。这意味着它可以有效地编译成与#2相同的格式,但要简洁一些。我更喜欢这个解决方案。

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

  •  类似资料: