This is not something fancy or magical about Kotlin at all, but I was asked about this multiple times in the past few months, so I think this may be something worth to share.
The following code will not compile in Kotlin, and IDE shows an error: Smart cast to ‘String’ is impossible, because ’name’ is a mutable property that could have been changed by this time:
If the warning message is already clear enough for you, you could stop reading right here :)
The rest of you might still wonder why the null check “does not work” or why the message says the property “could be changed by this time”?
The answer is quite straightforward: the property name
could be accessed by multiple threads at the same time, and therefore name
could be changed even if you had done null check around the usage.
Quick demo for the problematic null checks
|
|
From the console output we can see the null check just cannot guarantee name
won’t be null when you access it exactly as what IDE tries to warn you.
Solution #1: !!
with Thread Confinement
Yes the first solution is simply just put !!
in front of your property when accessing it!
You might think that’s a stupid idea, but it is actually a legit solution when you can limit “how your property is accessed”. Let’s change the sample code to demonstrate the idea:
|
|
The !!
will never throw NPE because we read and write the property from the same thread. If the property is always accessed from the same thread, we will never have multi-threading issues.
All Android developers should know this technique since Android framework throws this exception when you trying to accessing view methods from non-UI threads:
|
|
Now you should understand why Android framework has this limitation. If framework developers have to worry about multi-threading environment, the codebase will become very difficult to maintain and the overall performance will decrease because of all the required synchronizations.
This Thread Confinement also applies to all the typical lifecycle methods in Android. For example framework only calls onStart()
and onStop()
on main (UI) thread, so you can feel confident to access private properties if the property is only accessed by those lifecycle methods. (assuming you will never call those lifecycle methods manually…)
Solution #2: Make a local copy
If you really don’t care about the absolute correctness or you just want to get rid of the compiler error, you can create a local variable referencing the property first:
|
|
This compiles and runs as you would expect since variable name
is only visible to the current thread and therefore the value is guaranteed to be non-null inside the null check.
Note this does not eliminate the concurrency issue but creating a “snapshot” of the interested property so you can use safely.
Java concurrency
Java actually has a dedicated package for concurrency: java.util.concurrency. I am not going to cover all of them here. If you are interested in the concurrency topic, Java Concurrency in Practice is a very good book to read. It covers the basis of concurrency and the usages many java.util.concurrency
classes. Go buy the book and happy coding!