Null Safety

Kotlin Fundamentals — Null Safety
Kotlin Fundamentals
Course 1 · Chapter 3 · Null Safety

🛡️ Null Safety

Null safety is Kotlin's most famous feature, and the one most directly aimed at Java's biggest historical pain point — the NullPointerException. Kotlin bakes "can this be null?" directly into the type system, so the compiler catches most null-related bugs before the program ever runs. This chapter covers nullable types, the safe call operator, the Elvis operator, smart casts, and the escape hatch you should rarely reach for.

❓ Nullable Types

In Kotlin, a type is non-nullable by default. To allow null, the type must be explicitly marked with a trailing ?:

var name: String = "Philip" name = null // Compile error: String is non-nullable var nickname: String? = "Phil" nickname = null // Fine — String? explicitly allows null

This is the core idea of the whole chapter: String and String? are genuinely different types to the compiler. Every function, property, and variable declares up front whether null is a possibility — and the compiler then forces you to handle that possibility before you can use the value.

Null Handling: Java vs JavaScript vs Kotlin

LanguageCan any reference be null?Compiler enforcement
JavaYes, any object referenceNone — NPE is a runtime crash
JavaScriptYes, plus a separate undefinedNone — TypeError at runtime
KotlinOnly types explicitly marked with ?Compile error if unchecked

🔗 The Safe Call Operator: ?.

Calling a method or property directly on a nullable type is a compile error — Kotlin won't let you risk it. The safe call operator ?. calls through only if the value isn't null, and evaluates to null otherwise:

val nickname: String? = null // nickname.length // Compile error: nickname might be null println(nickname?.length) // null — safely short-circuits, no crash val realNickname: String? = "Phil" println(realNickname?.length) // 4

Safe calls chain naturally: user?.address?.city walks the chain and evaluates to null the moment any link in it is null, instead of crashing partway through — the same idea as JavaScript's optional chaining (user?.address?.city), which was directly inspired by this Kotlin feature.

🎸 The Elvis Operator: ?:

The Elvis operator provides a fallback value when the left-hand side is null — named for the way ?: looks like Elvis Presley's hairstyle turned sideways:

val nickname: String? = null val displayName = nickname ?: "Anonymous" println(displayName) // Anonymous // Combined with safe call — a very common pattern: val length = nickname?.length ?: 0 println(length) // 0 — safe call gives null, Elvis supplies the fallback

?. — "call it if it's there"

Short-circuits to null instead of crashing when the receiver is null. Read a?.b as "get b from a, but only if a exists."

?: — "or fall back to this"

Supplies a default when the left side is null. Read a ?: b as "use a, or b if a is null." The two operators combine constantly: a?.b ?: c.

🎯 Smart Casts

After an explicit null check, the compiler "remembers" that a value can't be null within that scope, and lets you use it as the non-nullable type automatically — no manual cast required:

fun printLength(text: String?) { if (text != null) { // Inside this block, Kotlin smart-casts text from String? to String println(text.length) // no ?. needed here — compiler already knows it's not null } else { println("No text provided") } }

This is one of Kotlin's quieter but most useful conveniences: Java requires an explicit cast even after a null check (or a local variable copy to avoid a "value could change" warning), while Kotlin's compiler tracks the null-checked state of a val automatically.

⚠ The Not-Null Assertion !! — Use Sparingly

The !! operator forces a nullable value to be treated as non-null, and throws a NullPointerException immediately if it turns out to be null after all: nickname!!.length. It exists for cases where you're certain a value can't be null but the compiler can't prove it — but reaching for it regularly defeats the entire point of Kotlin's null safety system. Prefer ?. and ?:; treat !! as a last resort, not a habit.

💻 Coding Challenges

Challenge 1: Safe Call Chains

Declare a nullable val city: String? = null. Use println with the safe call operator to print its length — it should print null without crashing. Then set city to "London" in a second variable and print its length again, this time getting an actual number.

Goal: See ?. short-circuit safely on both a null and a non-null value.

→ Solution

Challenge 2: Elvis Operator Defaults

Write a function displayName(nickname: String?): String that returns the nickname if present, or "Anonymous" otherwise, using the Elvis operator in a single-expression function. Call it with both a null and a non-null argument and print both results.

Goal: Practice ?: as a compact default-value pattern.

→ Solution

Challenge 3: Smart Casts

Write a function describeAge(age: Int?) that checks with an if whether age is not null. Inside the non-null branch, use the smart-cast value directly (no ?. or !!) to print whether it's even or odd. In the else branch, print "Age unknown".

Goal: See the compiler treat a null-checked nullable value as non-nullable automatically.

→ Solution

💡 This Is Why Kotlin Won Over Android Developers

Null safety is consistently cited as the single biggest reason teams migrated from Java to Kotlin for Android work — NullPointerExceptions were historically one of the most common crash causes in Android apps. Moving null-checking from "something you remember to do" to "something the compiler enforces" eliminates an entire category of bugs at the source.

🎯 What's Next

Next chapter: Classes & Data Classes — constructors, properties, data classes, copy(), and companion objects.