Computer scienceProgramming languagesKotlinControl flowExceptionsHow to work with exceptions

Avoiding NPEs. Null safety

8 minutes read

Null safety is a crucial feature in Kotlin's design, addressing the notorious 'Billion Dollar Mistake'—the use of null references in programming. Kotlin's type system aims to get rid of null references, which commonly lead to bugs and crashes in software development. Let's learn how to work with nullable types.

Safe calls

Suppose you have a nullable String and want to get its length. In Kotlin, you can't directly get it in a simple way:

val name: String? = "Kotlin"
val length = name.length // Compilation error 

If you need to have access to properties or call methods of a nullable object, you must check if it's null first. You can do it like this:

val name: String? = "Kotlin"
val length = if (name != null) name.length else null 

But it's quite a long expression! You can get the same result if you use the safe call operator ?. that allows you to access properties or call methods on nullable variables without risking a NullPointerException:

val name: String? = "Kotlin"
val length = name?.length // length is null if name is null

That's it! Just add a ? sign every time right after a nullable reference. ?. will compare its value to null and return null if that reference is null. It's even more convenient for chains. Here is an example of two safe calls in a row:

val street = city?.address?.street // the same as the next expression

val street = if (city != null && city.address != null) 
    city.address.street else null

Elvis Operator

Kotlin has another interesting way of handling nullable variables. Let's take a look at this snippet:

var name: String? = "Kotlin"
val length: Int? = name?.length
print(if (length != null) length else 0)

As you see, length variable will be null in case of null in the name variable. But if we want to print 0 if length is null, we need to add an additional check. It's a bit clumsy. However, we can simplify the code with the Elvis operator:

var name: String? = "Kotlin"
val length: Int? = name?.length
print(length ?: 0)

The Elvis operator works like this: if the left-hand side of the expression (name?.length) is not null, return it; otherwise, return the right-hand side (0). You can also use return and throw expressions in the right part:

val length: Int = name?.length ?: throw Exception("The name is null")

You may ask, but why is it called Elvis? Look: there he is!

The Not-Null Assertion Operator

There is an easy way to invoke an NPE: the not-null assertion operator!!. The code won't crash only if you're 100% sure that your variable won't be null:

var name: String? = "Kotlin"
print(name!!.length)

The code above looks like it's screaming, trying to scare the compiler. The piece of code above is almost equivalent to the one below:

var name: String? = "Kotlin"
val length: Int = name?.length ?: throw NullPointerException()
print(length)

This operator is used to stop the program when null is encountered. The !! operator asserts that the value is non-null, and if it's actually null, a NullPointerException is thrown immediately. This is contrary to Kotlin's philosophy of null safety and should be used sparingly. Why !! overuse is problematic:

  1. Defeats Null Safety: Kotlin's type system is designed to handle nulls gracefully. Overusing !! bypasses these safety measures.

  2. Code Smell: Regular use often indicates a disregard for proper null checks and can lead to code that's less stable and more challenging to maintain.

  3. Unexpected Crashes: It can cause your application to crash unexpectedly if proper null checks are not done before using !!.

Best Practices for Null Safety in Kotlin

Kotlin's type system is designed to eliminate the danger of null references from the code, also known as the Billion Dollar Mistake. Here are some best practices to enforce null safety:

  1. Use Nullable Types Wisely: Only use nullable types (Type?) when a variable can indeed be null. If a variable should not be null, use a non-nullable type.

  2. Safe Calls (?.): Use the safe call operator when accessing properties or methods on a nullable object. This will return null if the object is null instead of throwing a NullPointerException.

    val length = nullableString?.length
  3. Elvis Operator (?:): The Elvis operator lets you offer an alternative value if an expression evaluates to null.

    val length = nullableString?.length ?: 0
  4. Non-Null Assertion (!!): Use this operator only when you are certain that the value is not null. It will throw a NullPointerException if the value is null.

    val length = nullableString!!.length

By following these practices and utilizing Kotlin's standard library functions, you can write more robust and null-safe code. Remember, the key to avoiding NullPointerException is to write code that communicates nullability explicitly and handles it gracefully.

Conclusion

Kotlin's null safety features aim to reduce the risks associated with null references. By making types non-nullable by default and providing explicit nullable types, Kotlin compels developers to handle the possibility of null values in a controlled way. Use safe calls and the Elvis operator to handle nullable types securely and prevent NullPointerException.

421 learners liked this piece of theory. 15 didn't like it. What about you?
Report a typo