Null Safety in Depth
The billion-dollar mistake
In many languages, any object can secretly be null, and calling a method on a null object crashes the app with a NullPointerException (NPE). The inventor of null famously called it his “billion-dollar mistake”. Kotlin fixes this at the language level: by default, a variable cannot hold null, and the compiler forces you to handle the cases where it can.
Nullable vs non-null types
var a: String = "hi"
// a = null // ❌ compile error — String can never be null
var b: String? = "hi" // the ? makes it nullable
b = null // ✅ allowed
That single ? changes everything: now the compiler knows b might be null and won’t let you use it carelessly.
The tools for working with nullables
val name: String? = getName()
// 1. Safe call — returns null instead of crashing
println(name?.length)
// 2. Elvis operator — provide a default when null
val len = name?.length ?: 0
// 3. let — run a block only if not null
name?.let { println("Hello, $it") }
// 4. Not-null assertion — throws if null (use very rarely!)
val forced = name!!.length
Smart casts
Once you check that something isn’t null, Kotlin “smart casts” it to a non-null type inside that block — no extra work needed:
fun printLength(text: String?) {
if (text != null) {
// here the compiler KNOWS text is not null
println(text.length)
}
}
A real example
data class User(val name: String, val email: String?)
fun sendEmail(user: User) {
val address = user.email ?: run {
println("${user.name} has no email")
return
}
// from here on, address is a non-null String
println("Sending to $address")
}
Common mistakes
- Reaching for
!!to silence the compiler — it just moves the crash to runtime. Use?.and?:instead. - Making everything nullable “just in case” — design so values are non-null whenever possible.
- Forgetting that platform types from Java may be null — treat Java return values carefully.
Summary: Add?only when null is truly possible, then handle it with?.,?:andlet. Avoid!!. This is what makes Kotlin apps crash far less than Java ones.