Kotlin null safety

Distinguish nullable from non-null types, and use ?., ?:, let, and !! deliberately.

Kotlin
fun describe(name: String?): String {
    val length = name?.length ?: -1
    return name?.let { "name '$it' has $length characters" } ?: "no name"
}

fun main() {
    println(describe("Ada"))
    println(describe(null))

    val items: List<String?> = listOf("a", null, "c")
    println(items.filterNotNull().joinToString("+"))

    val text: String? = "kotlin"
    println(text!!.uppercase())

    val missing: String? = null
    val result = runCatching { missing!!.length }
    println(result.exceptionOrNull() is NullPointerException)
}
Output
name 'Ada' has 3 characters
no name
a+c
KOTLIN
true

Explanation

In Kotlin a type is non-null by default: a String can never be null, and the compiler enforces it. A nullable type is written with a question mark, String?, and the compiler refuses to call methods on it until you have dealt with the null case. This moves a whole class of NullPointerException errors from run time to compile time.

The safe-call operator ?. calls a member only if the receiver is not null and otherwise gives null. The Elvis operator ?: supplies a default for a null result. let runs a block with the value only if it is not null. filterNotNull removes nulls from a collection. The not-null assertion !! tells the compiler to trust you and throws a NullPointerException if you are wrong, so use it rarely.

Explore the dry run
Guided walkthroughReady
1fun describe(name: String?): String {
2 val length = name?.length ?: -1
3 return name?.let { "name '$it' has $length characters" } ?: "no name"
4}
5
6fun main() {
7 println(describe("Ada"))
8 println(describe(null))
9
10 val items: List<String?> = listOf("a", null, "c")
11 println(items.filterNotNull().joinToString("+"))
12
13 val text: String? = "kotlin"
14 println(text!!.uppercase())
15
16 val missing: String? = null
17 val result = runCatching { missing!!.length }
18 println(result.exceptionOrNull() is NullPointerException)
19}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
?. gives null for a null name, and ?: replaces that null with -1.—
—
let runs only when name is not null. Otherwise the Elvis operator returns the fallback text.—
—
A non-null name goes through let.—
name 'Ada' has 3 characters
A null name takes the fallback.—
name 'Ada' has 3 characters
no name
filterNotNull drops the null and changes the type to List<String>.—
name 'Ada' has 3 characters
no name
a+c
!! asserts that text is not null. Here it is, so the call works.—
name 'Ada' has 3 characters
no name
a+c
KOTLIN
!! on a null value throws, and runCatching captures the exception.—
name 'Ada' has 3 characters
no name
a+c
KOTLIN
The assertion failed with a NullPointerException.—
name 'Ada' has 3 characters
no name
a+c
KOTLIN
true

Cost and common mistakes

Each operation is O(1); filterNotNull is O(n).

  • Platform types from Java code can be null even though Kotlin does not mark them. Treat values from Java APIs with care.
  • !! turns the compiler's protection off. Prefer ?., ?:, or an early return when a value is missing.
  • lateinit and the null check inside a lambda interact with smart casts. The compiler smart-casts a val after a null check, but not a var that another thread could change.

Try it yourself

What does null?.length return?

Reveal answer and reasoning

null. The safe call skips the property access when the receiver is null, and the result type is Int?.

Further reading