And the Kotlin newbie asks, "why won't the following code compile?":
var left: Node? = null fun show() { if (left != null) { queue.add(left) // ERROR HERE } }
Smart cast to 'Node' is impossible, because 'left' is a mutable property that could have been changed by this time
I get that left
is mutable variable, but I'm explicitly checking left != null
and left
is of type Node
so why can't it be smart-casted to that type?
How can I fix this elegantly? :)
Between execution of n.left != null
and queue.add(n.left)
another thread could have changed the value of n.left
to null
.
To workaround this you have several options. Here are some:
Use a local variable with smart cast:
val left = n.left if (left != null) { queue.add(left) }
Use a safe call such as one of the following:
n.left?.let { left -> queue.add(left) } n.left?.let { queue.add(it) } n.left?.let(queue::add)
Use the Elvis operator with a jump to the next step of the enclosing while
loop:
queue.add(n.left ?: continue)
There is a fourth option in addition to the ones in mfulton26's answer.
By using the ?.
operator it is possible to call methods as well as fields without dealing with let
or using local variables.
Some code for context:
var factory: ServerSocketFactory = SSLServerSocketFactory.getDefault(); socket = factory.createServerSocket(port) socket.close()//smartcast impossible socket?.close()//Smartcast possible. And works when called
It works with methods, fields and all the other things I tried to get it to work.
So in order to solve the issue, instead of having to use manual casts or using local variables, you can use ?.
to call the methods.
For reference, this was tested in Kotlin 1.1.4-3
, but also tested in 1.1.51
and 1.1.60
. There's no guarantee it works on other versions, it could be a new feature.
Using the ?.
operator can't be used in your case since it's a passed variable that's the problem. The Elvis operator can be used as an alternative, and it's probably the one that requires the least amount of code. Instead of using continue
though, return
could also be used.
Using manual casting could also be an option, but this isn't null safe:
queue.add(left as Node);
Meaning if left has changed on a different thread, the program will crash.