智能转换到'类型'是不可能的,因为'是否是此时已经可以更改的可变属性

Kotlin新手问,“为什么下面的代码不能编译?”:

var left: Node? = null
    

fun show() {
if (left != null) {
queue.add(left) // ERROR HERE
}
}
Smart cast to 'Node'是不可能的,因为'left'是可变的 此时可以更改的属性

我得到left是可变变量,但我显式检查left != nullleft是类型Node,所以为什么不能智能强制转换为该类型?

我该如何优雅地解决这个问题?

151849 次浏览

left != nullqueue.add(left)执行之间,另一个线程可以将left的值更改为null

要解决这个问题,您有几个选择。以下是一些例子:

  1. 使用智能强制转换的局部变量:

     val node = left
    if (node != null) {
    queue.add(node)
    }
    
  2. 使用安全电话,例如:

     left?.let { node -> queue.add(node) }
    left?.let { queue.add(it) }
    left?.let(queue::add)
    
  3. 在外围函数中使用猫王运营商return返回:

     queue.add(left ?: return)
    

    注意,breakcontinue可以类似地用于循环中的检查。

除了mfulton26的答案之外,还有第四个选项。

通过使用?.操作符,可以在不处理let或使用局部变量的情况下调用方法和字段。

一些上下文代码:

var factory: ServerSocketFactory = SSLServerSocketFactory.getDefault();
socket = factory.createServerSocket(port)
socket.close()//smartcast impossible
socket?.close()//Smartcast possible. And works when called

它与方法、字段和其他所有我试图让它工作的东西一起工作。

因此,为了解决这个问题,你可以使用?.来调用这些方法,而不必使用手动强制类型转换或使用局部变量。

作为参考,这是在Kotlin 1.1.4-3中测试的,但也在1.1.511.1.60中进行了测试。不能保证它在其他版本也适用,它可能是一个新功能。

在你的例子中不能使用?.操作符,因为它是一个传递的变量,这就是问题所在。Elvis操作符可以作为一种替代,而且它可能是需要最少代码的操作符。除了使用continue,还可以使用return

使用手动强制转换也可以是一个选择,但这不是空安全的:

queue.add(left as Node);

这意味着如果将已经改变了留在另一个线程上,程序将崩溃。

你也可以使用lateinit如果你是确定,你将在以后的onCreate()或其他地方进行初始化。

使用这个

lateinit var left: Node

而不是这个

var left: Node? = null

当你像这样使用变量时,还有其他方法使用!!结束变量

queue.add(left!!) // add !!

这样做:

var left: Node? = null


fun show() {
val left = left
if (left != null) {
queue.add(left) // safe cast succeeds
}
}

这似乎是公认答案提供的第一个选项,但这正是您要寻找的。

尝试使用非空断言操作符…

queue.add(left!!)

这种方法不起作用的实际原因与线程无关。关键是node.left被有效地转换为node.getLeft()

这个属性getter可以定义为:

val left get() = if (Math.random() < 0.5) null else leftPtr

因此,两个调用可能不会返回相同的结果。

var left: Node? = null更改为lateinit var left: Node。问题解决了。

对于属性的智能强制转换,属性的数据类型必须是包含该方法的类或你想访问的行为,而不是属性是超类的类型。


例如在Android上

是:

class MyVM : ViewModel() {
fun onClick() {}
}

解决方案:

From: private lateinit var viewModel: ViewModel
To: private lateinit var viewModel: MyVM

用法:

viewModel = ViewModelProvider(this)[MyVM::class.java]
viewModel.onClick {}

GL

我会怎么写:

var left: Node? = null


fun show() {
val left = left ?: return
queue.add(left) // no error because we return if it is null
}

你最优雅的解决方案必须是:

var left: Node? = null


fun show() {
left?.also {
queue.add( it )
}
}

这样就不需要定义一个新的、不必要的局部变量,也不需要任何新的断言或强制类型转换(它们都不是DRY)。其他作用域函数也可以工作,所以选择您最喜欢的。

这对我很有用: private lateinit var varName: String

请执行如下操作:-

var left: Node? = null

使用空安全调用

left?.let { node -> queue.add(node) } // The most preferred one