Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 8

Safe Calls for Nullable Types

Recommended resources

Hello! Let's continue our journey into Kotlin's null safety system.

In the previous lesson, we established the fundamental difference between non-nullable types (like String) and nullable types (String?). You saw how Kotlin's compiler prevents you from directly accessing properties or methods on a nullable type, forcing you to handle the possibility of null and thereby preventing NullPointerExceptions at compile time.

This lesson addresses the next logical question: how do you safely access those properties? We will focus on the most common and idiomatic tool for this purpose: the safe call operator (?.). For a Java developer, this operator is a powerful and concise alternative to the frequent if (variable != null) checks.

Learning Outcome: Use the safe call operator (?.) to safely access properties or functions of a nullable type.


1. The Traditional Approach: Explicit Null Checks

Before diving into Kotlin's specific operators, let's start with a method that will be very familiar to you from Java: an explicit if statement.

If you have a nullable variable, you can check if it's not null before you use it.

fun main() {
    val name: String? = "Kotlin"
    
    val length: Int
    if (name != null) {
        length = name.length
    } else {
        length = -1 // Or some other default value
    }
    
    println("The length is $length") // Output: The length is 6
}

Inside the if block, the Kotlin compiler is smart. It performs a "smart cast," recognizing that name cannot be null within that scope, and allows you to access name.length as if name were a non-nullable String.

This approach is perfectly valid and safe. However, Kotlin provides a more concise and expressive way to achieve the same result.


2. The Safe Call Operator: ?.

The safe call operator, ?., is the cornerstone of working with nullable types in Kotlin. It provides a clean and readable way to execute an action only when a value is non-null.

The logic is simple:

  • If the value to the left of ?. is not null, the property or method to the right is accessed/called as usual.
  • If the value to the left of ?. is null, the expression short-circuits and evaluates to null immediately, without executing what's on the right.

Let's watch a short video that demonstrates this operator in action.

Kotlin Null Safe Operators. Safe Call, with Let, Elvis & Non-null operator. Kotlin Tutorials #12.1

This video from Smartherd provides a clear, practical demonstration of the safe call operator.

Watch the segment from 01:50 to 03:20. Notice how the output changes when the variable is null versus when it holds a string value.

As the video shows, the expression name?.length handles both cases elegantly.

val name: String? = "Kotlin"
println(name?.length) // Output: 6

val noName: String? = null
println(noName?.length) // Output: null

An important detail to note is the type of the resulting expression. Because the safe call can return null, the result is always a nullable type. In the example name?.length, the length property is an Int, but the type of the entire expression name?.length is Int?.

For a more detailed explanation and examples, the official Kotlin documentation is an excellent resource.

Safe call operator

This section of the Kotlin documentation formally defines the safe call operator and its behavior.

Please read the section titled 'Safe call operator'. Pay close attention to the explanation of the return type and the concept of chaining safe calls, which we will discuss next.


3. Chaining Safe Calls

One of the most powerful features of the safe call operator is its ability to be chained. This is particularly useful when navigating object graphs where several nested objects could be null. In Java, this often leads to a series of nested if checks, sometimes called the "pyramid of doom."

Consider this structure, common in backend applications:

class Department(val head: Employee?)
class Employee(val name: String)

Now, imagine you have a nullable Employee who may or may not belong to a Department, which may or may not have a head.

val employee: Employee? = // ... get employee from somewhere

To get the name of the department head, you can chain safe calls:

val headName = employee?.department?.head?.name

This single line of code is equivalent to the following Java-style checks:

String headName = null;
if (employee != null) {
    Department department = employee.getDepartment();
    if (department != null) {
        Employee head = department.getHead();
        if (head != null) {
            headName = head.getName();
        }
    }
}

The Kotlin chain is not only more readable but also safer. If employee, department, or head is null, the entire expression employee?.department?.head?.name gracefully evaluates to null, and headName will be null.


4. Code Exercise: Putting It into Practice

Time to apply this concept. Open your IntelliJ IDEA project and create a new Kotlin file named SafeCalls.kt.

  1. Define the data structures.
    Start by defining two simple classes. Notice that address is a nullable property of User.

    class Address(val street: String, val city: String)
    class User(val name: String, val address: Address?)
    
  2. Add the main function and test the scenarios.
    Copy the following code into your main function. Before running it, predict what each println statement will output.

    fun main() {
        // Scenario 1: User with a complete address
        val user1 = User("Alice", Address("123 Main St", "Anytown"))
    
        // Use the safe call operator to get the street.
        // What is the type of the 'street1' variable? (Hint: Hover over it in IntelliJ)
        val street1 = user1.address?.street
        println("Street for ${user1.name}: $street1")
    
        println("---")
    
        // Scenario 2: User with no address
        val user2 = User("Bob", null)
    
        // Use the safe call operator on a user with a null address.
        val street2 = user2.address?.street
        println("Street for ${user2.name}: $street2")
    }
    
  3. Run the code and verify your predictions. This exercise should solidify your understanding of how ?. behaves with both non-null and null objects.



Conclusion

In this lesson, you've learned how to use the safe call operator (?.) to work with nullable types in a concise and safe manner. This is a fundamental tool in any Kotlin developer's toolkit and a significant improvement over traditional null-handling patterns.

Key Takeaways:

  • The safe call operator (?.) is the preferred way to access properties or call methods on a nullable variable.
  • If the object is not null, the operation proceeds; if it is null, the expression evaluates to null.
  • The result of an expression using a safe call is always a nullable type (e.g., String?, Int?).
  • Safe calls can be chained together (object?.prop1?.prop2) to safely navigate object graphs, elegantly avoiding nested if-not-null checks.

Next Lesson Preview:

The safe call operator is excellent, but it always results in either the desired value or null. What if you want to provide a default value instead of null? In the next lesson, we will explore the Elvis operator (?:), which does exactly that.