Kotlin Null Safety

This tutorial is about Null Safety in Kotlin.

Null Safety in Kotlin is to eliminate the risk of occurrence of NullPointerException from code.

One of the most common pitfalls in many programming languages, including Java, is that accessing a member of a null reference will result in a null reference exception. In Java this would be the equivalent of a NullPointerException or NPE.

Let’s understand with simple example in Java where NullPointerException occurs:

Example:

class Student {
    public static void main(String[] args) {
        String name = null;
        System.out.println("Student name : " + name);
        System.out.println("Student name (Uppercase): " + name.toUpperCase());
    }
}

Output:

Student name : null
Exception in thread "main" java.lang.NullPointerException
at Student.main(Exp.java:6)

Kotlin’s comes with null safety to eliminate NullPointerException’s from our code.

Nullable and Non-Null References

In Kotlin, the type system differentiates between two types of references:

  1. Nullable Reference: These references can hold null values.
  2. Non-Null Reference: These references can’t hold null values.

In Kotlin, all variables are non-nullable by default. We cannot assign a null value to a variable because it’ll throw a compilation error(NPE):

Example:

fun main() {
    var name:String = "Arun"
    var name:String = null   // COMPILATION ERROR
}

Output:

Error:(6, 27) Kotlin: Null can not be a value of a non-null type String

Here, by default compiler considers name variable as a Non-Null reference. So, we cannot assign null to it.

To define a nullable variable, we must append a question mark(?) to the type declaration:

fun main() {
    var name: String? = null   // NO ERROR
}

We can call a method or access a property on a non-nullable variable. However, in the case of nullable variables, we need to handle the null case explicitly. Otherwise, it will throw a compilation error since Kotlin knows that the variable contains null references:

fun main() {
    var name:String? = "Arun"
    println(name.length)     // ERROR
}

Output:

Error:(7, 21) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Here compiler is not allowing us to find length of the String directly because it may throw Null Pointer Exception.

Working With Nullable Types

Let’s look at the different ways how we can handle null references safely in Kotlin.

  • Check null in conditions
  • Safe calls
  • The Elvis operator
  • The !! operator

Null Check

We can use the if-else expression to explicitly check for nullable variables.

Example:

fun main() {
    var name:String? = "Arun"
    if (name != null)
        println(name.length)
    else 
        null
}

Output:

4

Now compiler won’t give any error as we have already checked null condition first. It is the most basic way to deal with Nullable Reference.

Safe Call Operator(?.)

Kotlin has a safe call operator(?.) to handle null references.

This operator executes any action only when the reference has a non-null value. Otherwise, it returns a null value.

The following expression:

name?.length

is equivalent to:

if (name != null)
     name.length
 else
     null

Example:

fun main() {
    var name: String? = "Ninja"
    println("Name length: ${name?.length}")

    name = null
    println("Name length: ${name?.length}")
}

Output:

Name length: 5
Name length: null

Using let() Method

We can use the let() method along with the safe call operator to act on a non-nullable variable:

Example:

fun main(args: Array<String>) {
    val cities: List<String?> = listOf("Kolkata", null, "Mumbai", "Delhi", null, "Banglore")
    var newlist: List<String?> = emptyList()
     for (city in cities) {
         city?.let {
             newlist = newlist.plus(it)
         }
     }
     println(newlist)

Output:

[Kolkata, Mumbai, Delhi, Banglore]

Using also() Method

We can use the also() method to execute additional operations like logging and printing of the non-nullable variables. This method can be used in a chain with let() or run() method.

Here’s how we can use also() method along with let() method:

Example:

fun main(args: Array<String>) {

    val cities: List<String?> = listOf("Kolkata", null, "Mumbai", "Delhi", null, "Banglore")
    var newlist: List<String?> = emptyList()
   
    for (city in cities) {
        city?.let {
            newlist = newlist.plus(it)
            it
        }?.also { it -> println("$it") }
    }
    println(newlist)

Output:

Kolkata
Mumbai
Delhi
Banglore
[Kolkata, Mumbai, Delhi, Banglore]

Using run() Method

We can use the run() method to execute some operations on a non-nullable reference. This method operates using this reference and returns the value of the lambda result:

Example:

fun main(args: Array<String>) {

    val cities: List<String?> = listOf("Kolkata", null, "Mumbai", "Delhi", null, "Banglore")
    var newlist: List<String?> = emptyList()
    for (city in cities) {
        city?.run {
            newlist = newlist.plus(this)
            this
        }?.also {it-> println("$it") }
    }
    println(newlist)

Output:

Kolkata
Mumbai
Delhi
Banglore
[Kolkata, Mumbai, Delhi, Banglore]

Elvis Operator (?:)

We can use the Elvis operator(?:) to return a default value only if the original variable has a null value.

If the left-side expression of the Elvis operator has a non-nullable value, then it is returned. Otherwise, the right-side expression is returned.

The following expression:

name?.length ?: -1

is equivalent to:

if (name != null)
     name.length
 else
     -1

Example:

fun main() {
    var name: String? = "Arun"
    println("Name length: ${name?.length ?: -1}")

    name = null
    println("Name length: ${name?.length ?: -1}")
}

Output:

Name length: 4
Name length: -1

We can also use throw and return expression in the right-side expression of the Elvis operator. So instead of default values, we can throw specific exceptions in the right-side expressions of the Elvis operator:

var name: String? = null
val nameLength = name?.length ?: throw IllegalArgumentException("invalid length")

Not Null Assertion Operator ( !! )

We can use the not-null assertion operator(!!) to explicitly throw a NullPointerException.

This operator converts any reference to its non-nullable type and throws an exception if the reference has a null value.

Example:

fun main() {
    var name: String? = "Arun"
    println("Name length: ${name!!.length}") // prints 4

    name = null
    println("Name length: ${name!!.length}") // Throws NullPointerException
}

Output:

Name length: 4
Exception in thread "main" kotlin.KotlinNullPointerException
at ExampleKt.main(Example.kt:44)
at ExampleKt.main(Example.kt)

Leave a Reply