kotlinbeginner

Kotlin Null Safety Complete Guide

Master null safety: safe calls, elvis operator, let/also/run, smart casts, and nullable collection handling.

kotlin
fun main() {
    var name: String? = "Alice"
    var missing: String? = null

    // Safe call operator ?.
    println(name?.length)    // 5
    println(missing?.length) // null (no NPE)

    // Safe call chain
    val city = getUser()?.address?.city?.uppercase()
    println("City: $city")

    // Elvis operator ?: (default value)
    val len = missing?.length ?: 0
    println("Length: $len")

    // let: execute block if non-null
    name?.let { println("Name is: $it") }
    missing?.let { println("Never printed") }

    // let for transformation
    val upper = name?.let { it.trim().uppercase() } ?: "UNKNOWN"
    println("Upper: $upper")

    // Smart cast after null check
    if (name != null) {
        println("Length: ${name.length}") // compiler knows it's String
    }

    // when with null
    fun describe(value: String?): String = when {
        value == null -> "null"
        value.isBlank() -> "blank"
        value.length < 3 -> "short"
        else -> "normal: $value"
    }
    println(describe(null))
    println(describe(""))
    println(describe("hi"))
    println(describe("hello"))

    // Nullable collections
    val items: List<String?> = listOf("a", null, "b", null, "c")
    val nonNullItems: List<String> = items.filterNotNull()
    println("Non-null: $nonNullItems")

    // mapNotNull
    val numbers = listOf("1", "abc", "3", "xyz", "5")
    val parsed = numbers.mapNotNull { it.toIntOrNull() }
    println("Parsed: $parsed") // [1, 3, 5]

    // requireNotNull and checkNotNull
    val config: String? = "production"
    val env = requireNotNull(config) { "Config must be set" }
    println("Env: $env")

    // Safe cast
    val obj: Any = "Hello"
    val str: String? = obj as? String  // safe cast
    val num: Int? = obj as? Int        // null, no ClassCastException
    println("Cast: str=$str, num=$num")

    // takeIf / takeUnless
    val validated = name
        ?.takeIf { it.length >= 3 }
        ?.also { println("Valid: $it") }
    println("Validated: $validated")
}

data class Address(val city: String?)
data class User2(val name: String, val address: Address?)
fun getUser(): User2? = User2("Alice", Address("NYC"))

Use Cases

  • Safe handling of nullable data from APIs
  • Default value patterns with elvis operator
  • Filtering and transforming nullable collections

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.