9. Nullable Types

9.1. A First Example

Kotlin’s readLine() function can be used to read a line of text from standard input. Let’s investigate what it returns when called.

  1. In the Kotlin REPL, invoke readLine() and assign the returned value to a variable. Kotlin will wait for input at the point, so type some text and press Enter. Then query the value of the variable by entering its name at the prompt. Your interaction with the REPL should resemble this:

    >>> val input = readLine()
    Hello
    >>> input
    res1: kotlin.String? = Hello
    

    Note, in particular, the type reported for the variable: String?.

  2. Try querying the length property of the string. For example, if your variable is named input, then enter input.length at the REPL prompt. What happens?

  3. The error message seen in the previous step notes that input is a ‘nullable receiver’. If an object is of a nullable type, you cannot use the usual . operator to access a property or call a method; instead you must use the safe call operator, ?..

    Try entering input?.length at the prompt. What happens now? What type of value is returned? (Look closely!…)

  4. Why does readLine() return a nullable string? Repeat the REPL interaction shown in Step 1 above, but this time instead of entering any text, press Ctrl+D when Kotlin awaits your input. This closes the input stream before any input has been supplied. As a result, you should see that input has a value of null. Kotlin uses the null value to signal that there was no input.

    If you had just pressed the Enter key instead of Ctrl+D, the value of input would have been a zero-length string instead.

9.2. Creating Nullable Variables

If you wish to create your own nullable variable, append a question mark to the type name when declaring it.

  1. Try entering the following lines in the REPL:

    var name: String? = "Joe"
    name = null
    

    name is a nullable string, which means its value can be either a string or null.

  2. Repeat the above steps, using a type of String instead of String?. This time, the assignment to name will fail. You can only assign null to variables of a nullable type.

9.3. The Elvis Operator

Sometimes, we need to assign the result of computation to a variable and want that variable to be given a default value of some kind when the computation evaluates to null. The ‘null coalescing operator’ or Elvis operator, ?:, provides a neat way of achieving this.

  1. Enter the following at the REPL prompt:

    val input = readLine() ?: "NO INPUT"
    

    Enter some text, as before, then check the value and type of the input variable. The value should be the text you entered, and the type should be String, not String?.

  2. Repeat the previous step, but this time press Ctrl+D instead of supplying input. If you check the input variable again, this time it should have a value of "NO INPUT". Again, the type will be String, not String?.

    The Elvis operator requires an expression that can evaluate to null on the left and a default value on the right. If the expression on the left is not null, its value is used; otherwise, the value supplied on the right is used. Since this eliminates the possibility of a null result, the value returned by the Elvis expression is of a non-nullable type.

  3. The safe call and Elvis operators can be used together. Try this in the REPL:

    readLine()?.length ?: 0
    

    This will access the length property if readLine() returns a string, otherwise it will return 0. Consequently, you will see 0 displayed if you just press Enter or Ctrl+D, otherwise you’ll see a count of the number of characters you entered.

9.4. The let Scope Function

Kotlin provides several scope functions which allow you to execute a block of code within the context of a particular object. One of these, the let function, is often combined with the safe call operator to provide a neat way of making code execution conditional on a value being non-null 1.

  1. Download passcheck.kt. This is a mostly incomplete program that is supposed to check the validity of a password entered by the user. Edit the file and add the following code:

    print("Enter a password: ")
    val password = readLine()
    

    Remember that readLine() here will return a nullable string.

  2. Now add the following:

    password?.let {
      when {
        it in badWords -> println("Password forbidden")
        it.length < 8  -> println("Password too short")
        it.length > 24 -> println("Password too long")
        else           -> println("Password OK")
      }
    }
    

    The code in the lambda function supplied to let will execute only when the value of password is non-null. Inside this lambda function, the password variable can be referenced as it.

  3. Compile and run the program like so:

    kotlinc -include-runtime -d passcheck.jar passcheck.kt
    java -jar passcheck.jar
    

Footnotes

1

This could also be achieved by testing explicitly for null with an if statement, but using safe call + let is a bit more elegant.