null means "no value". A user might not have a middle name, a search might find nothing, a form field might be empty. In many languages, using a variable that turns out to be null crashes the program with a null pointer exception, so common that its inventor, Tony Hoare, called it his "billion-dollar mistake".
Kotlin tackles the problem at its root. A normal type like String can never be null. If a value might be missing, you must say so with a question mark: String?. The compiler then refuses to let you use a nullable value as if it were definitely there, so the check has to happen in your code, not as a surprise crash in your users' hands.
In this lesson you'll declare nullable types and handle them with Kotlin's tools: the safe call ?., the Elvis operator ?:, smart casts after an if check, ?.let blocks, and the !! operator you should almost never use.
Nullable and non-nullable types
var name: String = "Ada"
name = null // error: Null can not be a value of a non-null type String
var nickname: String? = "Addy"
nickname = null // fineStringalways holds a real string.String?holds a string ornull. Any type can be made nullable:Int?,Double?,List<String>?.- You can't call
nickname.lengthdirectly, because nickname might be null. The compiler insists you handle that case first.
Safe calls and the Elvis operator
The safe call ?. calls a function or reads a property only if the value isn't null; otherwise the whole expression is null:
val len: Int? = nickname?.length // null if nickname is nullThe Elvis operator ?: supplies a default when the left side is null:
val len: Int = nickname?.length ?: 0
val display = nickname ?: "Guest"Read a ?: b as "a, or if that's null, b". Chains like user?.address?.city ?: "Unknown" handle several missing values in one readable line.
Smart casts and let
After you check a value against null, Kotlin smart casts it to the non-null type inside that branch:
if (nickname != null) {
println(nickname.length) // allowed: nickname is a String here
}?.let { } runs a block only when the value isn't null, with the value available as it:
nickname?.let { println("Hi, $it") }You can also return early with Elvis: val email = user.email ?: return.
The !! operator
!! tells the compiler "trust me, this isn't null" and converts String? to String. If you're wrong, the program crashes with a NullPointerException at that line:
val length = nickname!!.lengthTreat !! as a warning sign. There's almost always a safer option: a safe call, an Elvis default, an if check or let. Seeing !! in a code review usually prompts the question "what happens if this is null?".
Working with lists that contain nulls
Collections often contain gaps: a list of parsed numbers where some input wasn't valid, or a list of optional middle names. Kotlin has functions that remove the nulls and give you a list of the non-null type:
val raw = listOf("12", "abc", "7")
val numbers = raw.mapNotNull { it.toIntOrNull() } // [12, 7]
val names: List<String?> = listOf("Ana", null, "Ben")
println(names.filterNotNull()) // [Ana, Ben]mapNotNull transforms each item and drops the ones that become null, which is perfect for parsing. filterNotNull simply removes nulls. Both return List<Int> or List<String>, so later code doesn't need any null checks.
The Elvis operator can also end a function early, which keeps the main logic simple: val user = findUser(id) ?: return "No such user". If the left side is null, the function returns straight away; otherwise user is a non-null value.
Kotlin Null Safety Syntax
Null-safety tools
var s: String? = null
s?.length // Int? (null if s is null)
s?.length ?: 0 // Int with a default
if (s != null) s.length // smart cast
s?.let { println(it) } // run only when not null
s!!.length // crash if null: avoid
"12".toIntOrNull() ?: 0Kotlin Null Safety Summary
- Types are non-null by default; add
?(String?) to allow null. ?.calls something only when the value isn't null, otherwise gives null.?:(Elvis) provides a default when the left side is null.- After
if (x != null), x is smart cast to the non-null type;x?.let { }runs only for non-null values. !!forces a non-null type and crashes if it's wrong; avoid it.
Kotlin Null Safety Examples
Each example shows the code, the output it produces and what each part does.
Greeting users who may not have a nickname
fun greet(name: String, nickname: String?) {
val shown = nickname ?: name
println("Hi, $shown! (nickname length: ${nickname?.length ?: 0})")
}
fun main() {
greet("Margaret", "Maggie")
greet("Linus", null)
}Hi, Maggie! (nickname length: 6)
Hi, Linus! (nickname length: 0)Line-by-line explanation
- Line 1name must always be given; nickname may be null.
- Line 2Elvis uses the nickname if there is one, otherwise the name.
- Line 3Safe call plus Elvis: the length, or 0 when there's no nickname.
Parsing input safely
fun main() {
val inputs = listOf("42", "seven", "", "15")
var total = 0
for (text in inputs) {
val n = text.toIntOrNull()
if (n == null) {
println("Skipping \"$text\"")
continue
}
total += n
}
println("Total: $total")
}Skipping "seven"
Skipping ""
Total: 57Line-by-line explanation
- Line 5toIntOrNull returns an Int? instead of throwing for bad text.
- Lines 6 to 9Handle the null case and skip to the next item.
- Line 10After the null check, n is smart cast to Int, so it can be added.
Chains, let and smart casts
data class Address(val city: String?)
data class User(val name: String, val address: Address?)
fun main() {
val users = listOf(
User("Ana", Address("Lisbon")),
User("Ben", Address(null)),
User("Cy", null)
)
for (u in users) {
val city = u.address?.city ?: "Unknown"
println("${u.name}: $city")
}
users[0].address?.city?.let { println("First user's city has ${it.length} letters") }
}Ana: Lisbon
Ben: Unknown
Cy: Unknown
First user's city has 6 lettersLine-by-line explanation
- Lines 1 to 2A data class is a compact way to hold data; you'll learn them later. city and address may both be missing.
- Line 11The chain stops at the first null and Elvis supplies a default.
- Line 14let runs only if the city exists, with it as the non-null String.
Common Kotlin Null Safety Mistakes
Calling a method on a nullable value
val email: String? = getEmail()
println(email.length) // error: Only safe (?.) or non-null asserted (!!.) calls are allowedprintln(email?.length ?: 0)Using !! to silence the compiler
val age = input.toIntOrNull()!! // crashes when input isn't a numberval age = input.toIntOrNull() ?: 0Making everything nullable
var title: String? = "Untitled" // never actually null, but every use must checkvar title: String = "Untitled"Only use ? when a value can genuinely be missing.
Kotlin Null Safety in Real Projects
Null safety in Android
Android apps deal with missing values constantly: a screen that hasn't loaded yet, an optional profile photo, a network response without a field. Google has reported that Android apps written in Kotlin have significantly fewer crashes than those in Java, and null safety is a large part of the reason. Kotlin also marks values coming from Java code specially, so you're reminded to check them.
Kotlin Null Safety FAQ
Can a Kotlin program still throw a NullPointerException?
Yes, in a few situations: using !! on a null value, reading a lateinit property before it's set, and values returned from Java code, whose nullability Kotlin can't always know. In ordinary Kotlin code without !!, the compiler prevents almost all of them.
What is the difference between ?. and !!?
?. is safe: if the value is null, the whole expression becomes null and the program continues. !! asserts the value isn't null and throws a NullPointerException if it is. Prefer ?. combined with ?: for a default, and keep !! for cases where null really would be a bug.
Should I use let or an if check for nullable values?
Both work. if (name != null) reads naturally and smart casts name inside the block. name?.let { ... } is handy at the end of a chain or when you want to use the result as an expression. Pick whichever is clearer in context; for a single check, if is often simplest.
Can the Elvis operator throw an exception?
Yes. Because throw is an expression in Kotlin, you can write val id = input.toIntOrNull() ?: throw IllegalArgumentException("Not a number: $input"). That's a clear way to reject invalid data while keeping id non-null.
What does a type ending in ! mean in error messages?
Types such as String! are platform types: values coming from Java code, where Kotlin doesn't know whether null is possible. Treat them carefully, and declare them as String? when the Java method might return null.
Kotlin Null Safety Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the declaration so middleName can be null.
var middleName: String = null
What does this print?
val city: String? = null
println(city ?: "Unknown")
What does this print?
val word: String? = "kotlin"
println(word?.uppercase()?.length)
What does s?.let { println(it) } do when s is null?
Why is !! usually a warning sign in code reviews?
Kotlin Null Safety Quiz
5 questions to check what you learned.
Can a variable of type
Stringhold null in Kotlin?- Yes
- Only inside main
- No, only String? can
- Only if it's a var
What type is
nickname?.lengthif nickname is a String??- Int?
- Int
- String
- Boolean
What is a smart cast?
- Converting a String to an Int
- A cast that never fails
- Casting with !!
- Kotlin treating a value as non-null after you've checked it isn't null
What does
"abc".toIntOrNull()return?- 0
- null
- An exception
- "abc"
What does
a ?: b ?: creturn when a is null, b is null and c is 3?- null
- An error
- 3
- b