Question
I am trying to understand what ?: does in Kotlin, for example in this code:
val list = mutableList ?: mutableListOf()
I also want to understand why it can be rewritten like this:
val list = if (mutableList != null) mutableList else mutableListOf()
What does the ?: operator mean here, and why are these two versions equivalent?
Short Answer
By the end of this page, you will understand Kotlin's Elvis operator (?:), how it works with nullable values, and why it is equivalent to a simple if expression that checks for null. You will also see where it is commonly used in real Kotlin code and how to avoid common mistakes.
Concept
Kotlin has built-in null safety. One of the main ideas in Kotlin is that a variable can either be:
- non-nullable: it must always contain a value
- nullable: it may contain a value or
null
The Elvis operator is written as ?:.
It means:
- use the value on the left if it is not null
- otherwise use the value on the right
So this code:
val list = mutableList ?: mutableListOf()
means:
- if
mutableListis notnull, assign it tolist - otherwise, create a new empty mutable list and assign that
This is equivalent to:
val list = if (mutableList != null) mutableList else mutableListOf()
The Elvis operator matters because handling null is extremely common in real programs. Data may be missing from:
- API responses
- databases
- user input
- configuration files
- optional function arguments
Instead of writing a full if/else every time, Kotlin gives you a short, readable way to provide a fallback value.
Mental Model
Think of the Elvis operator like a backup plan.
- First, try the value on the left.
- If that value exists, use it.
- If it is missing (
null), fall back to the value on the right.
A simple analogy:
nickname ?: "Guest"
This means:
- if the user has a nickname, use it
- otherwise, use
"Guest"
So ?: is like saying: "Use this, otherwise use that."
Syntax and Examples
The basic syntax is:
val result = nullableValue ?: fallbackValue
Example 1: Default string
val name: String? = null
val displayName = name ?: "Anonymous"
println(displayName)
Output:
Anonymous
Because name is null, Kotlin uses the value on the right side.
Example 2: Your list example
val mutableList: MutableList<String>? = null
val list = mutableList ?: mutableListOf()
println(list)
Output:
[]
If mutableList already had a value, that existing list would be used instead.
Example 3: Equivalent if expression
val mutableList: MutableList<String>? = null
val list = if (mutableList != null) {
mutableList
} else {
mutableListOf()
}
This does the same thing. In Kotlin, if is an expression, so it can return a value.
Example 4: Elvis with safe calls
A very common pattern is combining ?. and ?::
val length = text?.length ?: 0
This means:
- if
textis not null, gettext.length - otherwise, use
0
That is much shorter than:
val length = if (text != null) text.length else 0
Step by Step Execution
Consider this example:
val mutableList: MutableList<String>? = null
val list = mutableList ?: mutableListOf("A", "B")
println(list)
Step by step:
-
mutableListis declared asMutableList<String>?- The
?means it may benull.
- The
-
mutableListis assignednull- So right now, it does not point to any list.
-
Kotlin evaluates this expression:
mutableList ?: mutableListOf("A", "B") -
Kotlin checks the left side:
mutableList- Is it
null? - Yes.
- Is it
-
Because the left side is
null, Kotlin evaluates and uses the right side:mutableListOf("A", "B") -
listnow refers to a new mutable list containing"A"and"B". -
println(list)prints:[A, B]
Now compare with a non-null value:
val mutableList: MutableList<String>? = mutableListOf("X")
val list = mutableList ?: mutableListOf("A", "B")
println(list)
Step by step:
mutableListcontains"X"- Kotlin checks the left side of
?: - It is not null
- Kotlin uses
mutableList - The right side is not needed
- Output is:
[X]
This is why the Elvis operator is efficient and readable: it only uses the fallback when needed.
Real World Use Cases
The Elvis operator is used whenever a value might be missing and you want a default.
Common use cases
1. Default values for user input
val username = inputName ?: "Guest"
If the user did not provide a name, use a safe default.
2. Safe API data handling
val title = apiResponse.title ?: "Untitled"
API fields are often nullable.
3. Optional configuration values
val port = envPort ?: 8080
If no environment value exists, use a standard port.
4. Empty collections instead of null
val items = response.items ?: emptyList()
This avoids repeated null checks later.
5. Fallback object creation
val session = currentSession ?: createNewSession()
If there is no current session, create one.
6. UI display defaults
val subtitle = article.subtitle ?: "No subtitle available"
This prevents null from reaching the UI.
Real Codebase Usage
In real Kotlin projects, developers often use the Elvis operator in a few common patterns.
1. Guard clauses with early return
fun printName(name: String?) {
val safeName = name ?: return
println(safeName)
}
If name is null, the function returns immediately.
2. Throwing an error when a required value is missing
val token = config.token ?: throw IllegalStateException("Token is required")
This is very common when a value must exist.
3. Combining safe call and default
val itemCount = cart?.items?.size ?: 0
This safely navigates nested nullable values.
4. Replacing null collections with empty ones
val users = response.users ?: emptyList()
This makes later code simpler because you can iterate without checking for null.
5. Validation and fallback
val emailToUse = providedEmail ?: defaultEmail
Useful in service layers, controllers, and app setup code.
6. Constructor or mapping code
data class UserDto(val name: String?)
data class User(val name: String)
fun map(dto: UserDto): User {
return User(name = dto.name ?: "Unknown")
}
This is common when converting external data into safer internal models.
Common Mistakes
1. Thinking ?: is a ternary operator
In some languages, ? : is a ternary conditional operator. Kotlin does not have that style of ternary operator.
For example, Java has:
condition ? value1 : value2
But Kotlin's ?: is specifically the Elvis operator, used for null fallback.
2. Using it on non-nullable values
Broken idea:
val name: String = "Sam"
val result = name ?: "Anonymous"
This is pointless because name can never be null.
Use Elvis when the left side is nullable:
val name: String? = null
val result = name ?: "Anonymous"
3. Forgetting that the right side is the fallback
Broken understanding:
val number: Int? = 5
val result = number ?: 0
result is 5, not 0, because the left side is not null.
4. Confusing null with empty values
The Elvis operator only checks for null, not empty strings or empty lists.
val text: String? = ""
val result = text ?: "default"
println(result)
Output:
The result is still the empty string, because "" is not null.
5. Overusing Elvis when if is clearer
Sometimes an if expression is easier to read, especially for more complex logic.
val result = if (user != null && user.isActive) user.name else "Guest"
If the condition is more than just null fallback, if may be the better choice.
6. Forgetting type compatibility
Both sides of the Elvis operator should produce compatible types.
Potential issue:
val value: String? = null
val result = value ?: 123
Kotlin will infer a broader type such as Any, which may not be what you want.
Prefer matching types:
val value: String? = null
val result = value ?: "123"
Comparisons
Elvis operator vs if expression
| Approach | Example | Best for |
|---|---|---|
| Elvis operator | val name = input ?: "Guest" | Simple null fallback |
if expression | val name = if (input != null) input else "Guest" | When you want the logic written explicitly |
Both are equivalent for basic null checks.
Elvis operator vs safe call
| Operator | Example | What it does |
|---|---|---|
?. | user?.name | Accesses a property only if the object is not null |
?: | user?.name ?: "Unknown" | Provides a default if the left side is null |
They are often used together.
Elvis operator vs !!
| Operator | Example | Behavior |
|---|---|---|
?: | name ?: "Anonymous" | Safely provides a fallback |
!! | name!! | Forces non-null and may throw NullPointerException |
In most cases, ?: is safer than !!.
Elvis operator vs empty checks
| Situation | Example | Important note |
|---|---|---|
| Null check | name ?: "Guest" | Only handles null |
| Empty string check | if (name.isNullOrEmpty()) "Guest" else name | Handles null and "" |
Use the Elvis operator only when the problem is specifically null.
Cheat Sheet
Quick syntax
val result = nullableValue ?: fallbackValue
Meaning
- Use the left side if it is not
null - Otherwise use the right side
Equivalent form
val result = if (nullableValue != null) nullableValue else fallbackValue
Common patterns
val name = input ?: "Guest"
val length = text?.length ?: 0
val users = response.users ?: emptyList()
val token = config.token ?: throw IllegalStateException("Missing token")
val item = value ?: return
Rules to remember
?:is the Elvis operator- It is used for null fallback
- It is not the same as a ternary operator from other languages
- It only checks for
null, not empty strings or empty collections - It works especially well with
?.
Good defaults
- Strings:
"Unknown","" - Numbers:
0 - Lists:
emptyList()ormutableListOf() - Objects: a newly created default object
Watch out for
- Using Elvis on a non-nullable value
- Returning a fallback of the wrong type
- Assuming it handles empty values
FAQ
What does ?: mean in Kotlin?
It is the Elvis operator. It returns the value on the left if that value is not null; otherwise, it returns the value on the right.
Is Kotlin ?: the same as the ternary operator in Java?
No. Kotlin does not have Java's condition ? a : b ternary operator. Kotlin's ?: is specifically for handling null values.
Why is mutableList ?: mutableListOf() the same as an if check?
Because both expressions do the same null test:
- if
mutableListis not null, use it - otherwise, create a new mutable list
When should I use the Elvis operator in Kotlin?
Use it when a value might be null and you want to provide a default, return early, or throw an exception.
Can I use the Elvis operator with throw or return?
Yes. This is common in Kotlin:
val user = currentUser ?: return
val token = config.token ?: throw IllegalStateException("Missing token")
Does the Elvis operator check for empty strings or empty lists?
No. It only checks for null. An empty string or empty list is still considered a real value.
Is the right side always evaluated?
It is evaluated only when the left side is null. That is why it works well for fallback creation.
Mini Project
Description
Build a small Kotlin program that prepares user profile data for display. Some incoming values may be null, so you will use the Elvis operator to provide safe defaults. This demonstrates a very common real-world task: cleaning nullable data before using it in an application.
Goal
Create a Kotlin program that prints a safe, user-friendly profile summary even when some input fields are null.
Requirements
- Create nullable variables for a user's name, age, and list of hobbies.
- Use the Elvis operator to provide default values for missing data.
- Print the final safe values in a readable format.
- Use at least one example that combines
?.and?:.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.