Why does the smart-casting not work here? And how ...
# announcements
p
Why does the smart-casting not work here? And how would you solve it?
Copy code
fun main() {
    var value: Any? = "hello"

    if (value != null) {
        for (i in 1..10) {
            value = nonNullFoo(value) // smart casting fails
        }
    }
}

fun nonNullFoo(obj: Any): Any {
    return "World"
}
l
I believe it fails because
value
might have been changed and the compiler can't guarantee it wasn't
Personally, I would solve it with a beautiful
!!
Because I'm smarter than the compiler in this case, and I'm sure it isn't null
p
value
is only every assigned the result of
nonNullFoo
which is
Any
I guess the compiler just can't see that far
l
the compiler just can't see that far
I believe that's exactly it
The compiler can't trust that you didn't change
value
inside the for, I think
p
thanks
n
You could use
value?.let {...}
l
I think
let
will mask the fact that
value
can never be null. It will give the obvious possibility that it is null sometimes
1
h
The compiler looks further, actually. Who knows if
value
is being changed by another thread somewhere else in the code? Or even from some included dependency library? Contracts is the way to solve this.
p
how could
value
be changed by another thread when it's a local variable?
l
Value is local, other threads cant touch the reference
h
Admittedly! Don't know why I missed that, I just answered with the autopilot turned on, since this question arises from time to time, and mostly with code where a different thread actually may touch the
value
... I decompiled the code to take a look at the generated bytecode, and am now just as stumped as you are. My only explanation for this is that the compiler must be as blind as I was, and/or didn't dare insert a
Contract
all by itself.
We missed the important part because the red highlighting was in the wrong place!
The problem is that nonNullFoo accepts
Any
, not
Any?
. So it's the nonNullFoo function that should have had the red underlining, in my opinion ...
t
@poohbar I just wrote the same code in a scratch file and the smart cast works just as intended. This should only fail when dealing with
var
properties, what version of Kotlin are you using ?