skip to content

What does the safe-call operator ?. do in Kotlin, and what is the type of a?.length when a is a String??

level: juniorimportance: must knowfreq 90%

answer

  1. null receiver -> whole expression is null
  2. result type becomes nullable (T -> T?)
  3. short-circuits: member never evaluated when null
  4. default safe alternative to !!
  5. receiver evaluated once

basics

~20 s

?. only calls the method or reads the property if the value on the left is not null. If it is null, the whole expression is null instead of crashing. So a?.length gives an Int? (a number or null).

solid answer

~30 s

The ?. safe-call operator guards a member access. For a?.length, Kotlin first checks whether a is null. If a is null it short-circuits and yields null without evaluating .length; if a is non-null it accesses .length normally. Because either outcome is possible, the result type is nullable: a?.length is Int? even though String.length is a non-null Int. This is how you read members of a nullable receiver without a NullPointerException. Contrast with plain a.length (won't compile on a nullable a) and a!!.length (throws NPE if null). ?. is the safe, idiomatic default for nullable receivers.

code

kotlin · 4 lines
kotlin
val a: String? = null
val len: Int? = a?.length  // null, no NPE
val b: String? = "hi"
val len2: Int? = b?.length // 2

go deeper

for a junior

Knows ?. avoids NPE and the result is nullable.

for a middle

Explains short-circuit semantics and that receiver is evaluated once.

for a senior

Frames ?. against !!/?: in the broader null-safety model and type propagation.

for a principal

Discusses how null-safety types and ?. shape API design and reduce defensive null checks across a codebase.

## The safe-call operator `?.` In Kotlin the type system distinguishes **non-nullable** types (`String`) from **nullable** types (`String?`, the `?` suffix). You cannot dereference a nullable value directly — `a.length` where `a: String?` is a compile error. The **safe-call operator** `?.` lets you access a member of a possibly-null receiver safely. ### Semantics `a?.length` is equivalent to: *if `a` is `null`, the result is `null`; otherwise the result is `a.length`*. The receiver `a` is evaluated once. When it is null, `.length` is **never** evaluated (short-circuit). ```kotlin val a: String? = null val n: Int? = a?.length // n == null, no exception val b: String? = "hi" val m: Int? = b?.length // m == 2 ``` ### Result type is nullable Even though `String.length` returns a non-null `Int`, `b?.length` has type `Int?`: the safe call introduces the possibility of `null` into the result. Generally, for `receiver?.member` of declared type `T`, the expression type is `T?`. This propagates: you often pair it with the Elvis operator (`?:`) or `?.let` to collapse back to a non-null value, but those are separate operators. ### Contrast with neighbours - `a.length` — does **not compile** when `a: String?`. - `a!!.length` — the not-null assertion; throws `NullPointerException` if `a` is null. - `a?.length` — yields `null` silently if `a` is null. Use `?.` as the default safe way to touch members of a nullable receiver.

  • Why is a?.length nullable even though String.length is non-null?
    Because the safe call can itself produce null (when the receiver is null), so the overall type must admit null: Int?.
  • What happens if you write a.length instead?
    It fails to compile, because a is of type String? and Kotlin forbids dereferencing a nullable type directly.

Like asking 'if the door is there, knock' — no door, no knock, and you don't trip over a missing door.

saying these in an interview costs you the question

  • Saying a?.length returns a non-null Int
  • Claiming ?. throws an exception when the receiver is null
  • Confusing ?. (safe call) with !! (not-null assertion)
  • Thinking a.length compiles on a nullable receiver
  • Saying the member is still evaluated when the receiver is null

context