In Defense of !!
Kotlin offers fantastic language support for null safety, and that safety is one big reason that many developers are falling in love with it. However, I’ve seen a lot of distaste for the
!! operator and I believe that distaste is misplaced.
!! is used to take a nullable value and assert that it is non-null. This will throw a
NullPointerException if the value is indeed null.
When used without care we lack null safety. When used carefully it is a good way to tell the compiler “I know this isn’t actually null here.”
Where is it useful?
!! is useful when we know a nullable variable is not null. In the case of Android development, this is often the case in our components that have a lifecycle (e.g. Activities, Fragments, and Services).
Let’s take this Fragment for example:
Note that our
TextView is nullable. This is important. Outside of the Fragment lifecycle we cannot make any guarantees about
textView because it may not have been created yet, or it may have been destroyed.
However, within the Fragment lifecycle we can make guarantees about
textView. During (and after)
onViewCreated() we know that our views have been inflated, so unless something has gone horribly (one might say exceptionally) wrong our
textView will not be null.
Thus we can tell the compiler “hey, I know
textView can be null in some circumstances, but I know it isn’t at this point in time.”
lateinit is not appropriate for properties that have a lifecycle. In the above example,
lateinit would not be appropriate because a Fragment’s Views should be nullable. We should clear out our view references in
onDestroy() because at that point the Views are no longer valid.
Making the property a non-null
lateinit var breaks that. Once our View references are no longer valid we still hold on to them.
Why not use a safe call?
We could certainly achieve the proper behavior using a safe call (e.g.
textView?.id), but my problem with that approach is that it adds unnecessary null checks to the bytecode.
The overhead of a few extra null checks is negligible, but why do it at all when there is a perfectly fine way to tell the compiler that you know an optional reference is not null? A safe call is simply not necessary.
The second problem I have with using a safe call is that developers often ignore the case where the optional variable is indeed null. I have often seen code in which the developer slaps a
? on a nullable variable that is actually required for the code to work correctly.
Simply ignoring null values can make for broken code just as much as allowing null values. Allowing the code to throw an exception in cases where you absolutely need a non-null value is sometimes the right way to go.
I also don’t buy the argument that
foo?.bar() is better than
foo!!.bar() in these situations just because someone thinks
!! is inherently ugly or “bad.”
But !! makes my eyes bleed
I agree, using
!! isn’t ideal, but the problem is that Kotlin doesn’t currently have an idiomatic way of expressing properties that have a defined lifecycle (but it might soon! Keep reading!).
A proper solution would provide for three features:
- We can query whether the variable is valid
- We can tell the compiler that we know a variable is valid within a particular context
- We can reset the variable when it is no longer valid
Kotlin Android Extensions
There’s one more related issue I’d like to call out, and that is the Kotlin Android Extensions’ handling of null for views.
Lets take a look at an example:
Here we have a
Fragment that uses the Kotlin Android Extensions to reference a
TextView much in the same way we did in the last example.
The difference here is that the extensions provide synthetic access to non-nullable views instead of nullable ones. If we run this code, the application will crash with a
NullPointerException when the Fragment is destroyed. Pretending that views are not nullable is dangerous, and runs into the very problem many people try to avoid in using Kotlin.
This might seem a little silly to you because you know better than to try to access views in
onDestroy(), but any time you get an asynchronous callback you lose any guarantee that your view still exists. This includes callbacks as simple as click listeners.
Now that we can accept that those views actually are nullable, we run into the second problem, which is that we can’t query whether or not the views are valid. Since the properties are non-null and there is no way to query any metadata about those properties, we are stuck assuming they are always valid (or wrapping calls in a try-catch).
These problems are real, and the Android extensions don’t provide an satisfactory way to deal with them.
A better solution
There is currently a proposal for
lateinit property intrinsics that would address this issue. It provides for resettable
lateinit properties and also exposes whether or not
lateinit properties are initialized.
This proposal is great because:
- Views can be
lateinitand non-null. We can assign them when the views are created and treat them as non-null.
- When the views are no longer valid, we can clear out our references.
- Best of all, we can check whether the variable exists when we cannot make any assumptions regarding its availability (i.e. when we are outside the Activity/Fragment lifecycle).