Smart casting is a mechanism where the Kotlin compiler automatically "downgrades" the type after checking for a certain type or null. This allows properties and methods of the cast type to be used without explicit conversion.
fun demo(x: Any) { if (x is String) { println(x.length) // x is automatically cast to String } }
Works with:
is / !is operators (e.g., in if or when)if (x != null))Does not work:
val str: String? = getString() if (str != null) println(str.length) // smart cast
val something: Any get() = fetch() if (something is String) { println(something.length) // Error: smart cast is not possible }
Can you rely on smart cast for open or var properties of a class within methods?
No! Smart cast works only with local variables and val properties in final classes. For open (open) or var properties, the compiler is not sure if the value can change due to other threads or subclasses. Manual cast or a local variable is required for them.
open class Base { open var maybeString: Any? = "abc" fun check() { if (maybeString is String) { // println(maybeString.length) // Error: Smart cast is not possible val asString = maybeString as String println(asString.length) // Explicit cast } } }
Story
In one project, open var properties were used for screen logic data models. The programmer relied on smart cast after checking if (model is SomeType), but during compilation had to manually cast types, which reduced readability and added code duplication due to a lack of knowledge of smart cast limitations on var/open.
Story
When retrieving data through a getter (e.g., via a delegate or validation), smart cast for the value did not work. The developer did not understand the reasons and spent several hours debugging until discovering that the compiler does not apply smart cast to such getters as they can return different values between calls.
Story
In a project, when processing nullable values through if (obj != null), smart cast worked inside the branch, but when the code was parallelized, NullPointerException appeared due to accesses outside the guarantee area. This demonstrated insufficient understanding of the local action of smart cast and the specifics of multithreading scenarios with nullable variables.