1. Overview

Kotlin offers many features that make code safer and more concise. One of the unique aspects of Kotlin is its approach to handling nullability. The two operators at the core of this functionality are the double-bang (!!) and nullable operators (?). In this tutorial, we’ll explore the difference between the double-bang and nullable operators in Kotlin and how they impact the handling of nullable values in our code.

2. The Nullability Problem

Null represents the absence of a value in a programming language. A null reference typically means that a variable or object doesn’t point to any valid data in memory, and it’s often used to represent missing or undefined values. Incorrectly handling null references can lead to crashes, runtime errors, and unexpected behavior in a program. Kotlin addresses this problem by distinguishing between nullable and non-nullable types.

In Kotlin, we can explicitly mark a variable or a property as nullable by appending the nullable operator to its type:

val age: Int
val name: String?

In this example, name is a nullable variable, while age is a non-nullable variable. This distinction helps the compiler catch potential nullability issues at compile time.

3. The Double-Bang (!!) Operator

The double-bang (!!) operator, also referred to as the not-null assertion operator, is used to tell the compiler that we are certain a nullable type is not null at a given point in the code. When we use the double-bang, we’re telling the compiler to suppress its null-safety checks:

val name: String? = null
val length = name!!length

We’re using the double-bang operator to assert that name is not null. However, since name is explicitly initialized as null, this code will throw a NullPointerException at runtime because we’re attempting to access the length property on a null reference.

Unit testing this code will involve checking whether the code behaves as expected, which, in this case, would be to verify that it indeed throws a NullPointerException when name is null:

@Test
fun testNullPointerException() {
    val name: String? = null
    assertFailsWith<NullPointerException> {
        val length = name!!.length
    }
}

We’ve intentionally set name to null, and then we use assertFailsWith() to confirm a NullPointerException is thrown when trying to access the length property with !!.

4. The Nullable (?) Operator

The nullable operator (?) is used to safely access properties or call functions on nullable types. When we use this operator, we tell the compiler to perform a null check before accessing the property or invoking the function. If the value is null, the expression evaluates to null, preventing a NullPointerException.

Here’s an example:

val name: String? = null
val length = name?.length

In this case, the variable name has the type String? and its value is equal to null.

@Test
fun testStringLength() {
    val name: String? = null
    val length = name?.length
    assertNull(length)
}

In this test case, we’re checking if the length of a name initialized as null is indeed null. If the length is null, the test case will pass. This ensures that the safe call operator is working as expected when dealing with nullable variables.

5. Conclusion

In this article, we’ve discussed the double-bang (!!) and nullable operator (?) operators that serve different purposes when dealing with nullable types. While double-bang allows us to assert that a nullable value is not null, the nullable operator provides safe access to properties and functions while handling null values gracefully.

Understanding when to use each operator is crucial for writing robust and error-free Kotlin code. By following Kotlin’s null safety philosophy and using these operators appropriately, we can make our code more reliable and less prone to NullPointerExceptions.

As always, the full implementation of these examples is available over on GitHub.