Null safety

Kotlin · Essentials · 12 min

Null safety is Kotlin's signature feature; it shapes every API you write and read.

In Kotlin, types are non-nullable by default: a String can never hold null, and the compiler enforces it. To allow null, add ?: String?.

You can't call members on a nullable value directly. Instead:

  • Safe call s?.length returns null if s is null. Calls chain: user?.address?.city.
  • Elvis operator s?.length ?: 0 supplies a fallback. The right side can also be return or throw: val id = param ?: return.
  • Smart casts: after if (s != null), the compiler treats s as String inside the branch (for val locals and stable values).
  • ?.let { ... } runs a block only when the value is non-null.
  • Not-null assertion s!! throws a NullPointerException if s is null. Treat each !! as a code smell.

Nullable types work with generics and collections: List<String?> (elements may be null) differs from List<String>? (the list itself may be null). Helpers include filterNotNull(), firstOrNull(), toIntOrNull(), getOrNull(i).

lateinit var declares a non-null property initialized later (common with dependency injection and test setup); accessing it before assignment throws.

At the Java boundary, types from Java are platform types (String!) with unknown nullability; annotate Java code with @Nullable/@NonNull or be defensive.

Example

fun lengthOf(s: String?): Int = s?.length ?: 0

fun shout(s: String?): String {
    if (s == null) return "nothing"
    return s.uppercase()            // smart cast: s is String here
}

data class Address(val city: String?)
data class User(val name: String, val address: Address?)

fun main() {
    println(lengthOf(null))         // 0
    println(lengthOf("Kotlin"))     // 6
    println(shout(null))            // nothing

    val u = User("Ada", Address(null))
    println(u.address?.city ?: "unknown")       // unknown

    val maybe: String? = "42"
    maybe?.toIntOrNull()?.let { println(it * 2) } // 84
    val names: List<String?> = listOf("a", null, "b")
    println(names.filterNotNull())  // [a, b]
    val oops: String? = null
    runCatching { oops!!.length }.onFailure { println(it::class.simpleName) } // NullPointerException
}

Remember

  • T is non-null, T? may be null, enforced at compile time
  • ?. safe call, ?: Elvis fallback, ?.let for non-null blocks
  • Smart casts follow null checks automatically
  • !! throws NPE; avoid it

Watch out for

  • Sprinkling !! to silence the compiler instead of handling the null case.

Check yourself

  1. What is the type of s?.length when s is String??

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: kotlinlang.org/docs/null-safety.html