?: Elvis Operator
| Since: | Kotlin 1.0(2016) |
|---|
The ?: operator in Kotlin, known as the Elvis operator, returns the right-hand side value when the left-hand side is null. It lets you specify a default value for null in a concise way. You can also use return or throw on the right-hand side.
Syntax
val length = name?.length ?: 0 // Returns 0 if name is null
// Combined with return
fun processName(name: String?) {
val nonNull = name ?: return // Returns from the function if null
println(nonNull.uppercase())
}
// Combined with throw
fun requireName(name: String?): String {
return name ?: throw IllegalArgumentException("Name is required")
}
// Used in a chain
val value = a?.b?.c ?: "default"
Syntax Overview
| Syntax | Description |
|---|---|
| a ?: b | Returns b if a is null; otherwise returns a. |
| ?: return | Early-returns from the function when the value is null. |
| ?: throw exception | Throws an exception when the value is null. |
| ?: continue | Skips to the next iteration of a loop when the value is null. |
| ?: break | Exits the loop when the value is null. |
Sample Code
sample_elvis_operator.kt
fun getName(id: Int): String? = if (id == 1) "Ayanami Rei" else null
// Function that uses the Elvis operator to provide a default value
fun greet(name: String?) {
val displayName = name ?: "Anonymous"
println("Hello, $displayName!")
}
// Combined with return (guard clause pattern)
fun processUser(name: String?, age: Int?) {
val n = name ?: return // Returns immediately if null
val a = age ?: return
println("$n ($a years old)")
}
// Combined with throw
fun divide(a: Int, b: Int?): Int {
val divisor = b ?: throw ArithmeticException("Divisor is null")
return a / divisor
}
fun main() {
greet("Ikari Shinji") // Hello, Ikari Shinji!
greet(null) // Hello, Anonymous!
// Combining safe call with the Elvis operator
val name1 = getName(1)
val name2 = getName(99)
println(name1?.length ?: -1) // 10
println(name2?.length ?: -1) // -1
// Guard clause pattern
processUser("Soryu Asuka", 14) // Soryu Asuka (14 years old)
processUser(null, 14) // Nothing is printed
processUser("Katsuragi Misato", null) // Nothing is printed
// Combined with throw
try {
println(divide(10, 2)) // 5
divide(10, null) // Exception thrown
} catch (e: ArithmeticException) {
println("Exception: ${e.message}") // Exception: Divisor is null
}
// Chained calls with the Elvis operator
data class Address(val city: String?)
data class User(val address: Address?)
val user: User? = User(Address(null))
val city = user?.address?.city ?: "Unknown"
println("City: $city") // Unknown
// Usage with collections
val names = listOf("Ayanami Rei", null, "Ikari Shinji", null, "Soryu Asuka")
val result = names.map { it ?: "Anonymous" }
println(result) // [Ayanami Rei, Anonymous, Ikari Shinji, Anonymous, Soryu Asuka]
}
elvis_operator.kt
kotlinc elvis_operator.kt -include-runtime -d elvis_operator.jar java -jar elvis_operator.jar Hello, Ikari Shinji! Hello, Anonymous! 10 -1 Soryu Asuka (14 years old) 5 Exception: Divisor is null City: Unknown [Ayanami Rei, Anonymous, Ikari Shinji, Anonymous, Soryu Asuka]
Common Mistakes
NG example 1: Writing a verbose if/else for a null check when the Elvis operator would be more concise.
mistake1_verbose_null_check.kt
fun greet(name: String?) {
val displayName = if (name != null) name else "Anonymous" // verbose
println("Hello, $displayName!")
}
fun main() {
greet(null)
}
The command looks like this:
kotlinc mistake1_verbose_null_check.kt -include-runtime -d mistake1_verbose_null_check.jar java -jar mistake1_verbose_null_check.jar Hello, Anonymous!
Use the Elvis operator for a concise form.
fix1_elvis.kt
fun greet(name: String?) {
val displayName = name ?: "Anonymous"
println("Hello, $displayName!")
}
fun main() {
greet("Ikari Shinji")
greet(null)
}
The command looks like this:
kotlinc fix1_elvis.kt -include-runtime -d fix1_elvis.jar java -jar fix1_elvis.jar Hello, Ikari Shinji! Hello, Anonymous!
NG example 2: Putting a non-null value on the left side of ?: — the Elvis branch is never reached (may produce a warning or compile error).
mistake2_non_nullable_lhs.kt
fun getLabel(age: Int): String {
return age.toString() ?: "unknown" // age is a non-null Int, so ?: is never evaluated
}
NG example 3: Expecting ?: to handle empty strings — it only reacts to null.
mistake3_empty_string.kt
fun showPilot(name: String?) {
val display = name ?: "No pilot assigned"
println(display)
}
fun main() {
showPilot("") // Empty string is printed as-is (not replaced by the default)
showPilot(null) // No pilot assigned
}
The command looks like this:
kotlinc mistake3_empty_string.kt -include-runtime -d mistake3_empty_string.jar java -jar mistake3_empty_string.jar No pilot assigned
To treat empty strings as null too, use takeIf or isNullOrBlank.
fix3_null_or_blank.kt
fun main() {
val name: String? = ""
val result = name?.takeIf { it.isNotBlank() } ?: "Ayanami Rei"
println(result)
}
The command looks like this:
kotlinc fix3_null_or_blank.kt -include-runtime -d fix3_null_or_blank.jar java -jar fix3_null_or_blank.jar Ayanami Rei
Notes
The Elvis operator is a Kotlin-specific alternative to the ternary operator. name ?: "default" is equivalent to Java's name != null ? name : "default", but more concise.
By placing return or throw on the right-hand side, you can implement guard clause patterns — such as "early return on null" or "throw on null" — in a compact form. This is especially useful for validating null values at the beginning of a function.
For the basics of nullable types, see Nullable Types / ?. Operator. For forcing a non-null value, see !! Non-null Assertion.
If you find any errors or copyright issues, please contact us.