Null Safety in Kotlin
Null — a tiny word, but it carries immense significance in programming. It signifies the absence of value in a var
or val
, essentially saying, "Hey, there's nothing here!"
Nullability, on the other hand, defines whether a variable can hold the null value.
Even before you execute this code, Intellij warns you with a red underline that something is amiss.
Now, here’s where Kotlin shines! In many programming languages, like the mighty Java, null is a notorious troublemaker, often causing crashes with its non-existent value. The dreaded NullPointerExceptions, right? 😱
But hold on! Kotlin’s got your back. It offers many ways many ways to write code in order to minimize the probability of receiving these exceptions.
Kotlin takes the opposite approach on the problem of null. It doesn’t allow variables to be null
unless you say it's okay. This helps catch problems early on, so your program doesn't unexpectedly crash later.
Kotlin’s Explicit Null Type
In Kotlin, if you want a variable to hold null values, you will have to add ‘?
' to it's type like this:
var name: String?
This explicit null type system helps prevent null pointer exceptions (NullPointerExceptions) by making it clear in the code which variables can be null and which cannot. It encourages developers to handle null values safely, leading to more robust and predictable code.
Null Safety
Because Kotlin distinguishes between nullable and non-nullable types, the compiler is aware of the possibly dangerous situation of asking a variable defined as nullable type to do something when the variable might not exist. To shield against these dangers, Kotlin will prevent you from calling functions on a value defined as nullable until you have accepted responsibility for this unsafe situation, thus preventing a lot of possible exceptions.
Now to fix this error, we have multiple options. Let’s checkout each of them in detail.
Before that, a point to be noted here:
At runtime, objects of nullable type and objects of non-nullable types are treated the same. A nullable type isn’t a wrapper over non-nullable type. All checks are performed at compile time. That means there’s no runtime overhead of working with nullable types in Kotlin.
Option Zero: Use a non-nullable type
Non-nullable types guarantee that they contain a value that can have functions called on it. So whenever you declare a nullable variable, ask yourself, “Why do I need a nullable type here? Would a non-nullable type work just as well?” Often, you simply so not need null so avoiding it is the safest option.
Option One: Checking for null using conditionals
You can explicitly check if the variable is null or not. If it is not null, then only you should call the required functions.
This option is best for times when you have some complex logic to be executed when a variable is null. An if/else statement allows you to represent that complex logic in a more readable form.
Option Two: The Safe Call Operator (?.)
When the compiler encounters the safe call operator, it knows to check for a null value. If it finds one, it skips over the call and does not evaluate it, instead returns null.
Safe call in chains
Safe call operator can also work in chains. For instance, consider a scenario where you have a user object, and within it, there’s an address field containing a state field. You can safely reference it like this: user?.address?.state
.
Safe call using let
If you want to call multiple lines of code when the variable is not null then you can use let.
Using a safe call operator should be favored before using value != null as a means to guard against null, since it is a more flexible root to solve generally the same problem, but in less code.
Option Three: The non-null assertion operator (!!.)
This is a much more drastic option than the safe call operator and should generally not be used.
If you use !!., you are telling the compiler: “If I ask a non-existent thing to do something, I DEMAND that you throw a null pointer exception!!”.
There are situations when using the non-null assertion operator (also known as double bang operator) is appropriate like when you do not have control over the type of a variable, but you are sure that it will never be null.
Option Four: The null coalescing operator (?:)
Kotlin’s null coalescing operator (also known as the “Elvis operator” because of its resemblance to the Elvis Presley’s iconic hairstyle), ensures that a value is not null by providing a default (not null) value. It says, “If the thing on the left hand side of me is null, do the thing on the right hand side instead.
The elvis operator can also be used in conjunction with the let function in place of an if/else statement.
How is Nullability enforced?
Kotlin has strict patterns around nullness when compared to languages like Java. This is a boon when working exclusively in Kotlin, but is this pattern implemented? Do Kotlin;s rules still protect you when interoperating with a less strict language like Java? How does Kotlin maintain a null-safe environment?
Answering these questions require a dive into the decompiled Java bytecode
Kotlin has two mechanisms for ensuring that non-null parameters accept null arguments.
- Note the
@NotNull
annotations on non-primitive parameters like countryCode in our above example. - Koltin compiler goes a step further in guaranteeing that countryCode will not be null using a method called Intrinsics.checkNotNullParameter. This method is called on each non-nullable parameter and will throw an IllegalArgumentException if a null value manages to padd as an argument.
In short, any function that you declare in Kotlin will play by Kotlin’s rules about nullness, even when represented as Java code on the JVM.
That’s it for this article. Hope it was helpful! If you like it, please hit clap.
Other articles of this series: