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.