Skip to content
AgileLogics
6 min read

Null Safety

Prerequisites: Basic types and type inference

null means "no value". In many languages, any variable can unexpectedly be null, and using it crashes the program with a NullPointerException — famously called a "billion-dollar mistake". Kotlin's null safety prevents this at compile time.

Real-world analogy: a nullable value is a parcel that might be an empty box. Kotlin makes you check before you unpack it.

Nullable and non-nullable types

By default, a type can't hold null. Add ? to allow it:

fun main() {
    val name: String = "Ada"
    val nickname: String? = null

    println(name.length)
    println(nickname == null)
}
3
true

String and String? are different types. Trying to put null into a String doesn't compile: Null cannot be a value of a non-null type 'String'.

You can't use a nullable value directly

fun main() {
    val nickname: String? = "Addy"
    println(nickname.length)
}
error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'String?'.

That error is Kotlin protecting you: nickname might be null, so you must handle that case first. The next lesson shows the tools for it.

Where nulls come from

Many functions return a nullable value when something might not exist:

fun main() {
    println("42".toIntOrNull())
    println("forty".toIntOrNull())
    println(listOf<Int>().firstOrNull())
    println(mapOf("a" to 1)["b"])
}
42
null
null
null

Functions ending in OrNull return null instead of throwing an error, and looking up a missing map key gives null.

Common mistakes

  • Making everything nullable "just in case". Only add ? when a value can genuinely be missing — every nullable adds checks for the reader.