I was recently reviewing code with a developer that is learning Kotlin and they were adamant that:
“You should never use null. Null is BAD.”
Null has got a bad rap. Yes, it’s to be avoided in most code, but in Kotlin, null is part of the type system and the compiler will tell you when you haven’t handled it appropriately. However, just because it’s easy to identify null in Kotlin doesn’t mean we should use it everywhere. There are perfectly appropriate safe uses of null in Kotlin.
Valid usages of null in Kotlin
1) When Representing “No Value”
var user: User? = null
If you need a way to represent whether a value is initialized or whether it has no value, then null is appropriate in this case.
2) Consuming External Data Sources
If you need a way to represent whether a value is available or unavailable, then null is appropriate in this case as well.
Apps must consume external data sources that we don’t have full control over. When operating in a non-hermetic environment, we run into cases where some data sources are not reliable and may provide null content. Whether it is a REST API, or an Android System Service, representing data that may not be available as a nullable type is important because it allows us to create code paths to handle both cases.
Functional Purity
While it seems ideal to never have null in your code, it’s not always possible to avoid, and sometimes shouldn’t be avoided.
Using a Sealed Class to Avoid Using Null
sealed class UserWrapper { object UninitializedUser : UserWrapper() data class InitializedUser(val user: User): UserWrapper() }
Creating a sealed class for the sole purpose of avoiding the use of null introduces unnecessary boilerplate code.
Don’t get me wrong, sealed classes are one of my favorite features of Kotlin though, and are great if you need to handle more scenarios than just initialized or uninitialized, but if that’s all you need, then using null is a valid approach.
Using a Nullable Field
val user : User?
Using a nullable field is much more concise in your definition than a sealed class. It’s also more concise to use a ?. operator instead of an “if” or “when” statement to process your sealed class.
Offensive Programming
If you really want to, you can run around with a bunch of !! (double bangs) in your code. However, you are living dangerously and your code will crash if the value is null. This may be what you are looking for though if you are trying to follow practices of Offensive Programming.
//This will crash if the user is null handleNonNullUser(user!!)
Defensive Programming has us cover all possible code paths just in case it might occur. When you do have these extra code paths, provide some fallback logic or warning logs.
Ways of Handling Null
If you check once that the value is non-null, the Kotlin compiler is smart enough for you to write your code without providing any !!. Then process the non-null val after it’s been checked once, to avoid multiple checks throughout my code. Here are some ways to do this:
Null Comparison 😐
if (user != null) { handleNonNullUser(user) } else { Log.w(TAG, "user was null") handleNullUser(user) }
This is very Java-esque, but it does the trick.
Let 🙂
user?.let { //Work with non-null user handleNonNullUser(user) }
I like using let because it allows us to write much more idiomatic Kotlin. It does not allow us to handle null, “else” case though.
Early Exit 👍
fun handleUser(user : User?) { user ?: return //exit the function if user is null //Now the compiler knows user is non-null }
If the value is null, you can exit the method immediately, otherwise you can operate on the non-null value (because the Kotlin compiler is that smart).
Immutable Shadows 👥😲
Using immutable “variable shadows” a really cool way of providing null safety. It will do an “Early Exit” if the value is null, otherwise it will assign the non-null value of the var to an immutable val allowing the compiler to know the value is non-null. Thanks to Gabriel Peal for this tip!
var user : User? = null fun handleUser() { val user = user ?: return //Return if null, otherwise create immutable shadow //Work with a local, non-null variable named user }
Conclusion
There is no doubt that you will write better code if you try and stick with non-null and vals where you can. However, if you run into a scenario where using null is the right thing to do, then don’t fight it.
Code appropriately using one of the techniques I’ve shared in this article to handle null effectively. These techniques allow you to avoid having to use the !! operator which can lead to NullPointerExceptions. The whole point of having a nullable type in Kotlin is to allow you to avoid NullPointerExceptions at compile time, instead of them sneaking up on your at run time.
You may be thinking that this post is similar to Roman Elizarov’s post “Null is your friend, not a mistake“, and you would be right. His post inspired me to finally publish this, and you can read more of the story behind publishing this post here.