Kotlin Null Safety
Null means "no value" — nothing is there. In many programming languages, accidentally using a null value crashes the program with a NullPointerException. Kotlin prevents this at the language level by separating types that can hold null from types that cannot.
The Problem with Null
// In Java, this compiles but crashes at runtime: String name = null; int length = name.length(); // NullPointerException — app crashes! // Kotlin stops this at compile time: val name: String = null // ERROR: Null can not be a value of type String
Kotlin's type system forces you to handle null explicitly. You decide which variables can be null, and Kotlin makes sure you never forget to check.
Nullable Types — Adding a Question Mark
Add a ? after a type to allow null. A type without ? can never be null.
val a: String = "Hello" // Cannot be null val b: String? = null // Can be null val c: String? = "World" // Also valid for nullable type var score: Int = 10 // Cannot be null var topScore: Int? = null // Can be null
Diagram — Nullable vs Non-Nullable
Type String (non-nullable) Type String? (nullable) ┌────────────────────┐ ┌────────────────────┐ │ Only holds text │ │ Holds text OR null │ │ "Hello", "Kotlin" │ │ "Hello", null │ │ "" (empty string) │ │ "" (empty string) │ │ null → COMPILE │ │ null → OK │ │ ERROR │ │ │ └────────────────────┘ └────────────────────┘
Safe Call Operator — ?.
The safe call operator (?.) calls a method or accesses a property only if the value is not null. If the value is null, it returns null instead of crashing.
val name: String? = "Kotlin"
println(name?.length) // 6
val empty: String? = null
println(empty?.length) // null (no crash)
// Chaining safe calls
data class Address(val city: String?)
data class User(val name: String, val address: Address?)
val user: User? = User("Dev", Address(null))
println(user?.address?.city) // null — safe at every step
Elvis Operator — ?: Providing a Default
The Elvis operator (?:) provides a fallback value when the left side is null.
val input: String? = null
val displayName = input ?: "Anonymous"
println(displayName) // Anonymous
val length = input?.length ?: 0
println(length) // 0
// Chain with logic
val serverName: String? = null
val connectedTo = serverName ?: throw IllegalStateException("No server!")
Non-Null Assertion — !! (Use with Caution)
The !! operator tells Kotlin: "I am certain this is not null. Trust me." If you are wrong and the value is null, Kotlin throws a NullPointerException. Only use this when you are 100% sure the value exists.
val city: String? = "Bengaluru" println(city!!.length) // 9 — works fine val nothing: String? = null println(nothing!!.length) // KotlinNullPointerException — CRASH!
The !! is an escape hatch, not a solution. Avoid it unless you have no alternative.
Safe Cast — as?
The as? operator attempts to cast a value to a type. If the cast fails, it returns null instead of throwing an exception.
val obj: Any = "Hello" val str: String? = obj as? String // "Hello" val num: Int? = obj as? Int // null (not an Int, no crash) println(str) // Hello println(num) // null
let — Executing Code Only When Not Null
Combine ?. with let to run a block of code only when a nullable value is not null.
val email: String? = "user@estudy247.com"
email?.let {
println("Sending email to: $it")
// this block only runs if email is not null
}
val noEmail: String? = null
noEmail?.let {
println("This line never runs.")
}
Handling Nullable Collections
val items: List= listOf("Kotlin", null, "Android", null, "JVM") // Filter out nulls val nonNull: List = items.filterNotNull() println(nonNull) // [Kotlin, Android, JVM] // Safe iteration for (item in items) { println(item?.uppercase() ?: "MISSING") } // KOTLIN, MISSING, ANDROID, MISSING, JVM
Null Safety Decision Flowchart
You have a nullable value → val x: String?
│
▼
Does it need a default if null?
YES → use Elvis (?:)
x ?: "default"
NO → need to use the value safely?
YES → use safe call (?.)
x?.length
NO → are you 100% sure it's not null?
YES → use !! (risky)
NO → use let { } block
