A null check passes yet the smart cast refuses to compile — why, and how do you fix it?
render() checks the property against null, yet the compiler still rejects name.length with "smart cast to 'String' is impossible".
Constraints: name must stay a var property on the class (other code reassigns it), do not reach for !!, and render() must read one consistent value.
class Screen {
var name: String? = null
fun render(): Int {
if (name != null) {
return name.length // does not compile
}
return 0
}
}
Find the cause and fix it.
A smart cast needs proof the value cannot change between check and use. A mutable property may be reassigned by another thread, so it is refused. Read it into a local val, which does smart-cast; name?.length ?: 0 also works.
- ✗Thinking a smart cast works on any
var, a class property included - ✗Reaching for
!!instead of copying the value into a localval - ✗Believing a property with a custom getter can be smart-cast
- →Why does a property with a custom getter also refuse to smart-cast?
- →Would the same code compile if
namewere a localvarin the function?
Why the compiler refuses
A smart cast is not a runtime check but a compiler inference: after if (name != null) it is willing to treat the type as String instead of String? — but only if it can prove the value cannot change between the check and the use.
For a mutable class property there is no such proof: another thread (or code called in between) could reassign it. The same goes for a property with a custom getter, which may return a different value on every call. Both are refused; a local val is accepted.
The fix: a local val
class Screen {
var name: String? = null
fun render(): Int {
val n = name // read once, then it cannot change
return if (n != null) n.length else 0
}
}
n is a local val that nothing can reassign, so inside the if it smart-casts to String.
The same thing, idiomatically
fun render(): Int = name?.length ?: 0
The safe call reads name once and ?: supplies the fallback. No !! is needed here — it would only have turned the race into a NullPointerException.