Understanding Nullability in Kotlin

In this article, we will explore the concept of Nullability in Kotlin, looking into the importance of null safety, and providing different ways to handle nullability effectively in Kotlin.

April 24, 20246 min read

Introduction


One of the common sources of bugs and errors in programming is the construct of Nullability. Nullability leads to unexpected behavior sometimes comprising of program crashes which impact user experience, and the overall application usability. Nullability, a provision of the Kotlin programming language has provided an innovative approach that caters to null values eliminating the associated app crashes through efficient null handling techniques. Arguably, Nullability as a core feature in the Kotlin programming language exemplifies the language further increasing its versatility. In this article, we will explore the concept of Nullability in Kotlin, looking into the importance of null safety, and providing different ways to handle nullability effectively in Kotlin.

What are Nullable Types?

In many programming languages, attempting to access a member of a null reference results in a null reference exception. As such null references are unacceptable in programming. Kotlin, however, provides access to null references by specifying nullable types that are often denoted by the “?” question mark symbol appended after the type name when declaring a variable. Nullable Types, therefore refer to references that can hold null.

var firstName: String? = "Aluoch"

In the above variable, firstName is assigned a nullable String type. The implication, therefore, is that firstName can be set to null as shown below;

var firstName: String? = "Aluoch"

firstName = null

Unlike nullable types, non-nullable types do not accept null values. As such, to set a nullable type, one must indicate using the “?” operator when declaring a variable.

For example in the function below, a nullable type returns a value regardless of its assignment. the variable age when assigned 5, prints 5, and when assigned null prints null, instead of an exception.

fun main() {
  var age: Int? = 5
  println(age)
  
  age = null
  println(age)
}

/*
5
null
*/

Try Kodaschool for free

Click below to sign up and get access to free web, android and iOs challenges.

Sign Up

Accessing Null Values

While nullable are fun ways to prevent unprecedented crashes when working with Kotlin, nullable values require special calls to access elements, otherwise a crash could still occur. For instance, accessing the length of a nullable string requires a special call that caters to nullability.

fun main() {
    val lName: String? = "Aluoch"
    println(lName.length)
}

/*
Error: variable lName can be null
*/



In the above code, we get an error since we are not checking for nullability before accessing the variable lName.

Checking for Null

When working with nullable types, we must check for null values before performing any operations.

fun main() {
    val lName: String? = "Aluoch"

    if(lName !== null) {
        println(lName.length)
    }
}


The above code functions without a crash and prints the length of the variable lName.

It is important to note that null safety checks as performed above gives us the freedom to provide a default value for a null outcome. The implication of this is that we can provide a default value to represent null hence avoiding displaying null to the user interface eg instead of null we can set the value to 0 in our database which further enhances data management.

fun main() {
    val age: Int? = null

    if(age != null) {
        println(age)
    } else {
      println(0);
    }

}



In the above, code snippet, the output is 0 since our variable is declared null. So when we do a conditional check, we print 0 instead of null.

The Safe Call Operator (?.)

Kotlin through its nullability features also provides safe calls which are used to access null values without a crash. Safe calls are made using the safe call operator “ ?. ” The safe call operator checks for null values before returning the appropriate output.

fun main() {
    val fName: String? = null
    val lName: String? = "Aluoch"

    println(fName?.length)
    println(lName?.length)
}



In the above code snippet, the output is null and 6 respectively. Notably, Safe calls serve to prevent a crash when accessing the value of a nullable type.

The Elvis Operator (?:)

The Elvis operator in Kotlin is fundamental when accessing nullable types. The Elvis operator provides a shortened form of if.. else statement. In this regard, we can use a value if available else, we use the default indicated value.

fun main() {
    val lName: String? = null
      
    val lLength = lName?.length ?: 0

    println(lLength)
}



In the above use of the Elvis Operator, we get an output of 0 since our variable is a null value. Note the use of both the safe call and the Elvis operator above.

The not-null assertion Operator (!!)

The not-null assertion operator is another way to access nullable values. The not-null operator converts a non-nullable type and throws an exception if the value is null.

fun main() {
    val lName: String? = null

    val lLength = lName!!.length

    println(lLength)
}

/*
Exception in thread "main" java.lang.NullPointerException
 at MainKt.main(Main.kt:4)
 at MainKt.main(Main.kt)
*/



Noteworthy, the not-null assertion is a delicate operator as it can lead to a crash when the value is null as depicted above. Note this beats the purpose for which nullable types were introduced.

(Note: Avoid the not-null operator when dealing with nullable types unless necessary)

Why Nullable Types

As depicted above, nullability provides the developer with more predictable outcomes when working with null values, eliminating the crashes associated with null pointer exceptions. Moreover, data is better handled with nullable types as default values can be easily provided. Another reason to embrace nullable types is the concise nature of the code written, and the implication on readability.

Best Practices

1. Use nullable types only when necessary such as in non-required fields that may be queried for certain information.

2. Avoid the not-null operator (!!) as much as possible given its implication when a value is null.

3. The Elvis operator (?:) provides a better data management approach with default values and, hence is the better model for accessing nullable types.

4. The safe call operator (?.) should be used liberally for access to properties and methods of nullable types.

Laurine Aluoch

About Laurine Aluoch

Software Engineer