Groovy Elvis operator (?:) for concise default values. 10+ examples covering Elvis assignment, null handling, and real-world patterns. Tested on Groovy 5.x.
“A little less conversation, a little more action.” — The Elvis operator takes that motto seriously: less boilerplate, more results.
Dierk König, Groovy in Action
Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 15 minutes
If you have ever written a long chain of if (x != null) x else defaultValue statements, you already know how tedious null handling can get. The Groovy Elvis operator (?:) exists specifically to save you from that kind of boilerplate. It is one of those features that, once you start using it, you genuinely wonder how you ever lived without it.
The name comes from the resemblance of ?: to Elvis Presley’s hairstyle (tilt your head to the left and squint). It is a shortened form of the groovy ternary operator that handles null and falsy values in a single, elegant expression. In this post, we will walk through 10+ tested examples covering everything from basic defaults to advanced patterns like chaining and the Elvis assignment operator.
If you are interested in other Groovy operators that make null handling easier, check out our guide on the Groovy safe navigation operator next. And for applying operations across collections with minimal code, see the Groovy spread operator.
Table of Contents
What is the Elvis Operator?
The Groovy Elvis operator (?:) is a shorthand for the ternary operator when the condition and the “true” branch are the same expression. Instead of writing x ? x : y, you simply write x ?: y. That is it. Groovy evaluates the left side, and if it is “truthy” (not null, not false, not zero, not empty), it returns that value. Otherwise, it returns the right side.
This makes it a perfect tool for providing default values. According to the official Groovy operators documentation, the Elvis operator is one of the most commonly used operators in idiomatic Groovy code.
Here is the key thing to understand: the Elvis operator relies on Groovy’s concept of “truthiness.” In Groovy, the following values are considered falsy:
nullfalse- Empty strings (
"") - Empty collections (
[],[:]) - Zero (
0)
Everything else is truthy. This is important because it means the Elvis operator catches more than just null — it catches empty strings, empty lists, and zero as well.
Elvis Operator Syntax
Let us look at the syntax side by side with the traditional ternary to see exactly what it replaces.
Elvis Operator Syntax
// Traditional ternary operator def result1 = name != null ? name : "Unknown" // Shortened ternary (Groovy allows this) def result2 = name ? name : "Unknown" // Elvis operator -- same result, much cleaner def result3 = name ?: "Unknown" // All three lines do the same thing! // The Elvis operator is the idiomatic Groovy way
The pattern is simple: expression ?: defaultValue. If the expression is truthy, return it. If not, return the default value. No repetition of the expression, no verbose null checks.
10+ Practical Examples
Let us work through real examples you can copy and run. Every example has been tested on Groovy 5.x with actual output shown.
Example 1: Basic Default Value for Null
The most common use case: providing a fallback when a variable might be null.
Example 1: Basic Default for Null
// Null variable gets the default value
String name = null
def displayName = name ?: "Guest"
println "Display name: ${displayName}"
// Non-null variable keeps its value
String userName = "Alex"
def display = userName ?: "Guest"
println "Display: ${display}"
// Works with any type, not just strings
Integer port = null
def serverPort = port ?: 8080
println "Server port: ${serverPort}"
// The original variable is not modified
println "Original name is still: ${name}"
Output
Display name: Guest Display: Alex Server port: 8080 Original name is still: null
Notice that the Elvis operator does not modify the original variable. It simply evaluates and returns one of the two sides. This is pure expression-based logic with no side effects.
Example 2: Default Value for Empty Strings
Since empty strings are falsy in Groovy, the Elvis operator catches them too. This is incredibly useful for form validation and user input handling.
Example 2: Default for Empty Strings
// Empty string is falsy -- default kicks in
def input = ""
def value = input ?: "No input provided"
println "Empty string: ${value}"
// Whitespace-only string is NOT falsy (it's truthy!)
def spaces = " "
def spacesResult = spaces ?: "No input provided"
println "Spaces only: '${spacesResult}'"
// To catch whitespace, trim first
def trimmedResult = spaces.trim() ?: "No input provided"
println "Trimmed spaces: '${trimmedResult}'"
// Practical: form field validation
def email = ""
def username = "john_doe"
println "\nEmail: ${email ?: '(not provided)'}"
println "Username: ${username ?: '(not provided)'}"
Output
Empty string: No input provided Spaces only: ' ' Trimmed spaces: 'No input provided' Email: (not provided) Username: john_doe
Watch out for the whitespace-only string gotcha. A string with only spaces is still truthy because it is not empty. If you want to catch those, call trim() or strip() before the Elvis operator.
Example 3: Default Value for Empty Collections
Empty lists and maps are falsy in Groovy, making the Elvis operator a natural fit for providing default collections.
Example 3: Default for Empty Collections
// Empty list gets replaced with default
def tags = []
def effectiveTags = tags ?: ["general"]
println "Tags: ${effectiveTags}"
// Non-empty list keeps its values
def categories = ["groovy", "java"]
def effectiveCategories = categories ?: ["uncategorized"]
println "Categories: ${effectiveCategories}"
// Empty map gets replaced
def config = [:]
def effectiveConfig = config ?: [timeout: 30, retries: 3]
println "Config: ${effectiveConfig}"
// Non-empty map stays as is
def settings = [debug: true]
def effectiveSettings = settings ?: [debug: false]
println "Settings: ${effectiveSettings}"
// Null collection also gets default
List items = null
def effectiveItems = items ?: ["default-item"]
println "Items: ${effectiveItems}"
Output
Tags: [general] Categories: [groovy, java] Config: [timeout:30, retries:3] Settings: [debug:true] Items: [default-item]
This pattern is extremely common when working with method parameters. Instead of checking if (list == null || list.isEmpty()), you just use the Elvis operator and move on.
Example 4: Chaining Multiple Elvis Operators
You can chain multiple Elvis operators to create a fallback chain. Groovy evaluates them left to right and returns the first truthy value.
Example 4: Chaining Elvis Operators
// Try multiple sources for a value, in priority order
def envVar = null
def configFile = ""
def hardcoded = "localhost"
def host = envVar ?: configFile ?: hardcoded
println "Host: ${host}"
// First truthy value wins
def a = null
def b = null
def c = "found it"
def d = "also here"
def result = a ?: b ?: c ?: d
println "Result: ${result}"
// All null/falsy? Last default is returned
def x = null
def y = ""
def z = 0
def fallback = x ?: y ?: z ?: "final default"
println "Fallback: ${fallback}"
// Practical: resolve user display name
def nickname = null
def fullName = null
def email = "alex@example.com"
def defaultName = "Anonymous"
def displayName = nickname ?: fullName ?: email ?: defaultName
println "Display: ${displayName}"
Output
Host: localhost Result: found it Fallback: final default Display: alex@example.com
Chaining is powerful, but keep it readable. If you have more than three or four levels, consider extracting the logic into a method or using a list-based approach like [a, b, c, d].find { it }.
Example 5: Elvis Operator with Method Calls
The left side of the Elvis operator can be any expression, including method calls. The expression is evaluated only once.
Example 5: Elvis with Method Calls
// Method that might return null
def findUser(String id) {
def users = [U1: "Alice", U2: "Bob"]
return users[id]
}
// Elvis provides default when method returns null
def user1 = findUser("U1") ?: "Unknown User"
def user3 = findUser("U3") ?: "Unknown User"
println "User U1: ${user1}"
println "User U3: ${user3}"
// The method is called ONLY ONCE (not twice like in ternary)
callCount = 0
def expensiveCall() {
callCount++
return "expensive result"
}
def result = expensiveCall() ?: "default"
println "\nResult: ${result}"
println "Call count: ${callCount}" // Only 1!
// Elvis with map access
def config = [db: [host: "localhost", port: null]]
def dbHost = config.db.host ?: "127.0.0.1"
def dbPort = config.db.port ?: 5432
println "\nDB Host: ${dbHost}"
println "DB Port: ${dbPort}"
Output
User U1: Alice User U3: Unknown User Result: expensive result Call count: 1 DB Host: localhost DB Port: 5432
This is an important advantage over the manual x ? x : default pattern. With the ternary, the expression x is evaluated twice. With the Elvis operator, it is evaluated only once. This matters when the expression is an expensive method call or has side effects.
Example 6: Elvis Operator with the Groovy Truth for Numbers
Here is where things get tricky. Zero is falsy in Groovy, which means the Elvis operator will replace a valid zero with the default. You need to be aware of this when working with numbers.
Example 6: Elvis with Numbers (Watch Out!)
// Zero is falsy! Elvis will replace it
def score = 0
def displayScore = score ?: "No score"
println "Score (Elvis): ${displayScore}" // "No score" -- not what we wanted!
// If zero is a valid value, use explicit null check instead
def safeScore = score != null ? score : "No score"
println "Score (null check): ${safeScore}" // 0 -- correct!
// Same issue with negative zero and 0.0
def balance = 0.0
println "Balance: ${balance ?: 'N/A'}" // N/A -- probably wrong!
// Positive numbers are truthy
def temperature = 42
println "Temp: ${temperature ?: 'N/A'}" // 42
// Negative numbers are also truthy
def offset = -5
println "Offset: ${offset ?: 'N/A'}" // -5
// Best practice: only use Elvis for null/empty checks, not numbers
Integer nullableCount = null
def count = nullableCount != null ? nullableCount : 10
println "\nNull count with explicit check: ${count}"
// The correct approach for nullable numbers:
Integer quantity = null
def effectiveQty = quantity != null ? quantity : 10
println "Effective quantity: ${effectiveQty}"
Output
Score (Elvis): No score Score (null check): 0 Balance: N/A Temp: 42 Offset: -5 Null count with explicit check: 10 Effective quantity: 10
This is the single most important gotcha with the Groovy Elvis operator. If zero is a valid value in your domain, do not use Elvis. Use an explicit null check instead. The Elvis operator is best suited for strings, collections, and object references where null and empty are both “no value.”
Example 7: Elvis Operator in Closures and Collections
The Elvis operator shines when used inside closures for transforming collections with potential null values.
Example 7: Elvis in Closures and Collections
// Transform a list, providing defaults for null entries
def names = ["Alice", null, "Charlie", "", null, "Frank"]
def cleaned = names.collect { it ?: "Anonymous" }
println "Cleaned: ${cleaned}"
// Use with findAll to filter and default
def scores = [math: 85, science: null, english: 92, history: null]
def report = scores.collect { subject, score ->
"${subject}: ${score ?: 'Not taken'}"
}
println "\nReport:"
report.each { println " ${it}" }
// Combine with groupBy
def users = [
[name: "Alice", department: "Engineering"],
[name: "Bob", department: null],
[name: "Charlie", department: ""],
[name: "Diana", department: "Engineering"]
]
def grouped = users.groupBy { it.department ?: "Unassigned" }
println "\nGrouped by department:"
grouped.each { dept, members ->
println " ${dept}: ${members*.name}"
}
Output
Cleaned: [Alice, Anonymous, Charlie, Anonymous, Anonymous, Frank] Report: math: 85 science: Not taken english: 92 history: Not taken Grouped by department: Engineering: [Alice, Diana] Unassigned: [Bob, Charlie]
The collect { it ?: default } pattern is one of the most common Elvis idioms in Groovy. It cleanly replaces all null or empty entries in a collection with a sensible default in a single pass.
Example 8: Elvis Operator with Safe Navigation (?. and ?:)
The Elvis operator pairs beautifully with the safe navigation operator (?.). Together, they create a powerful null-safe chain with a default at the end.
Example 8: Elvis with Safe Navigation
// Combining ?. and ?: for null-safe property access with defaults
class Address {
String city
String country
}
class Person {
String name
Address address
}
// Person with full address
def alice = new Person(name: "Alice", address: new Address(city: "London", country: "UK"))
println "Alice's city: ${alice?.address?.city ?: 'Unknown'}"
// Person with null address
def bob = new Person(name: "Bob", address: null)
println "Bob's city: ${bob?.address?.city ?: 'Unknown'}"
// Null person entirely
Person nobody = null
println "Nobody's city: ${nobody?.address?.city ?: 'Unknown'}"
// Person with empty city
def charlie = new Person(name: "Charlie", address: new Address(city: "", country: "US"))
println "Charlie's city: ${charlie?.address?.city ?: 'Unknown'}"
// Chaining multiple safe navigations with Elvis fallback
def getDisplayLocation(Person p) {
return p?.address?.city ?: p?.address?.country ?: "Location not set"
}
println "\nAlice location: ${getDisplayLocation(alice)}"
println "Bob location: ${getDisplayLocation(bob)}"
println "Nobody location: ${getDisplayLocation(nobody)}"
Output
Alice's city: London Bob's city: Unknown Nobody's city: Unknown Charlie's city: Unknown Alice location: London Bob location: Location not set Nobody location: Location not set
The ?. and ?: combination is the standard Groovy pattern for safe property drilling with a fallback. Without these two operators, you would need multiple nested if statements. Learn more about the safe navigation operator in our dedicated post.
Example 9: Elvis Operator for Method Parameters with Defaults
The Elvis operator is often used inside methods to normalize parameters that callers might pass as null or empty.
Example 9: Elvis in Method Parameters
// Using Elvis to normalize method parameters
def greet(String name, String greeting) {
def effectiveName = name ?: "World"
def effectiveGreeting = greeting ?: "Hello"
return "${effectiveGreeting}, ${effectiveName}!"
}
println greet("Alice", "Hi")
println greet(null, "Hey")
println greet("", null)
println greet(null, null)
// Building a configuration with defaults
def buildConfig(Map overrides) {
def defaults = [
host: "localhost",
port: 8080,
debug: false,
logLevel: "INFO"
]
return [
host: overrides?.host ?: defaults.host,
port: overrides?.port ?: defaults.port,
debug: overrides?.debug ?: defaults.debug,
logLevel: overrides?.logLevel ?: defaults.logLevel
]
}
println "\nFull override:"
println buildConfig([host: "prod-server", port: 443, debug: true, logLevel: "WARN"])
println "\nPartial override:"
println buildConfig([host: "staging-server"])
println "\nNo override:"
println buildConfig(null)
Output
Hi, Alice! Hey, World! Hello, World! Hello, World! Full override: [host:prod-server, port:443, debug:true, logLevel:WARN] Partial override: [host:staging-server, port:8080, debug:false, logLevel:INFO] No override: [host:localhost, port:8080, debug:false, logLevel:INFO]
This configuration-with-defaults pattern is everywhere in Groovy projects. It is the idiomatic way to handle optional parameters. Note the use of overrides?.host (safe navigation) combined with ?: defaults.host (Elvis) to handle both a null map and a missing key.
Example 10: Elvis Operator in String Interpolation
You can use the Elvis operator directly inside GString interpolation for clean, inline defaults.
Example 10: Elvis in String Interpolation
// Direct use inside GString interpolation
def firstName = "Alex"
def lastName = null
def title = ""
println "Name: ${firstName} ${lastName ?: ''}"
println "Title: ${title ?: 'No title'}"
// Building a formatted address
def street = "123 Main St"
def apt = null
def city = "Springfield"
def state = "IL"
def zip = ""
def address = """
${street}${apt ? ', Apt ' + apt : ''}
${city}, ${state} ${zip ?: '00000'}
""".strip()
println "\nFormatted address:"
println address
// Logging with Elvis for context
def userId = "U-12345"
def sessionId = null
def requestId = "REQ-789"
println "\n[${requestId ?: 'NO-REQ'}] User ${userId ?: 'anonymous'} (session: ${sessionId ?: 'none'})"
Output
Name: Alex Title: No title Formatted address: 123 Main St Springfield, IL 00000 [REQ-789] User U-12345 (session: none)
Inline Elvis in GStrings keeps your template code clean and readable. No need for separate variables or multi-line conditionals just to handle null values in output.
Example 11: Elvis Operator with Boolean and Groovy Truth Edge Cases
Let us explore the full range of Groovy truth to understand exactly what values trigger the Elvis default.
Example 11: Groovy Truth Edge Cases
// All falsy values that trigger the Elvis default
println "null: ${null ?: 'DEFAULT'}"
println "false: ${false ?: 'DEFAULT'}"
println "empty str: ${'' ?: 'DEFAULT'}"
println "zero int: ${0 ?: 'DEFAULT'}"
println "zero long: ${0L ?: 'DEFAULT'}"
println "zero float: ${0.0f ?: 'DEFAULT'}"
println "zero double:${0.0d ?: 'DEFAULT'}"
println "empty list: ${[] ?: 'DEFAULT'}"
println "empty map: ${[:] ?: 'DEFAULT'}"
println "\n--- Truthy values that KEEP their value ---"
println "true: ${true ?: 'DEFAULT'}"
println "non-empty: ${'hello' ?: 'DEFAULT'}"
println "one: ${1 ?: 'DEFAULT'}"
println "negative: ${-1 ?: 'DEFAULT'}"
println "space: ${' ' ?: 'DEFAULT'}"
println "non-empty list: ${[1] ?: 'DEFAULT'}"
println "non-empty map: ${[a:1] ?: 'DEFAULT'}"
// Boolean false is falsy -- be careful!
def enabled = false
println "\nFeature enabled: ${enabled ?: 'not set'}"
// This prints "not set" even though false is a valid value!
// Use explicit null check for booleans:
Boolean enabledNullable = false
def status = enabledNullable != null ? enabledNullable : true
println "Feature (null-safe): ${status}"
Output
null: DEFAULT false: DEFAULT empty str: DEFAULT zero int: DEFAULT zero long: DEFAULT zero float: DEFAULT zero double:DEFAULT empty list: DEFAULT empty map: DEFAULT --- Truthy values that KEEP their value --- true: true non-empty: hello one: 1 negative: -1 space: non-empty list: [1] non-empty map: [a:1] Feature enabled: not set Feature (null-safe): false
This is the complete picture of Groovy truthiness. Keep this reference handy. The biggest surprises for newcomers are that zero, false, and empty strings all trigger the Elvis default. If any of these are valid values in your use case, use an explicit null check instead.
Elvis Assignment Operator (?=)
Groovy 3+ introduced the Elvis assignment operator (?=). While the regular Elvis operator returns a value without modifying anything, the Elvis assignment operator actually updates the variable if it is null or falsy.
Elvis Assignment Operator (?=)
// Regular Elvis -- does NOT modify the variable
def name = null
def display = name ?: "Guest"
println "name after Elvis: ${name}" // still null
println "display: ${display}" // Guest
// Elvis assignment -- DOES modify the variable
def username = null
username ?= "Guest"
println "\nusername after ?=: ${username}" // Guest (variable is updated!)
// If already truthy, ?= does nothing
def role = "Admin"
role ?= "User"
println "role after ?=: ${role}" // Admin (unchanged)
// Useful for lazy initialization
class AppConfig {
String theme
String language
int maxRetries
void applyDefaults() {
theme ?= "dark"
language ?= "en"
// Be careful: 0 is falsy, so this will overwrite 0!
// maxRetries ?= 3 // Don't use ?= for numbers where 0 is valid
}
}
def config = new AppConfig(theme: null, language: "fr", maxRetries: 0)
config.applyDefaults()
println "\nTheme: ${config.theme}" // dark (was null, got default)
println "Language: ${config.language}" // fr (was truthy, kept)
Output
name after Elvis: null display: Guest username after ?=: Guest role after ?=: Admin Theme: dark Language: fr
The Elvis assignment operator is perfect for lazy initialization and applying defaults to mutable objects. Think of it as “assign this default value, but only if the variable does not already have a meaningful value.” According to the Groovy operators documentation, this operator was added in Groovy 3.0 as a natural extension of the Elvis operator.
Elvis Operator vs Ternary Operator
Let us put the two operators side by side so you can see exactly when to use each one.
Elvis vs Ternary Comparison
def name = null
def score = 0
def active = false
// --- TERNARY: condition ? valueIfTrue : valueIfFalse ---
// Use when the true and false branches are DIFFERENT from the condition
println "Ternary: ${name != null ? name : 'Guest'}" // Guest
println "Ternary: ${score > 0 ? 'Passed' : 'Failed'}" // Failed
println "Ternary: ${active ? 'Active' : 'Inactive'}" // Inactive
// --- ELVIS: expression ?: default ---
// Use when you want the expression itself OR a default
println "\nElvis: ${name ?: 'Guest'}" // Guest
println "Elvis: ${score ?: 'No score'}" // No score (not 0!)
// KEY DIFFERENCE: Elvis treats 0 and false as falsy
// Ternary gives you full control over the condition
println "\n--- When they differ ---"
println "Elvis score: ${score ?: 99}" // 99 (0 is falsy)
println "Ternary score: ${score != null ? score : 99}" // 0 (explicit null check)
println "\nElvis active: ${active ?: true}" // true (false is falsy)
println "Ternary active: ${active != null ? active : true}" // false (explicit)
Output
Ternary: Guest Ternary: Failed Ternary: Inactive Elvis: Guest Elvis: No score --- When they differ --- Elvis score: 99 Ternary score: 0 Elvis active: true Ternary active: false
Rule of thumb: Use the Elvis operator when you want “this value, or a default if it is null/empty/falsy.” Use the ternary operator when you need full control over the condition or when zero and false are valid values. The groovy ternary operator gives you more control, while the groovy Elvis operator gives you more brevity.
Real-World Use Cases
Use Case 1: REST API Response Handling
When processing API responses, fields might be null, empty, or missing entirely. The Elvis operator helps you normalize everything cleanly.
Real-World: API Response Handling
// Simulating a parsed JSON API response
def apiResponse = [
user: [
name: "Alice",
email: null,
bio: "",
settings: [
theme: "dark",
language: null,
notifications: null
]
],
metadata: null
]
// Safely extract with defaults
def userName = apiResponse?.user?.name ?: "Anonymous"
def userEmail = apiResponse?.user?.email ?: "not provided"
def userBio = apiResponse?.user?.bio ?: "No bio yet"
def theme = apiResponse?.user?.settings?.theme ?: "light"
def lang = apiResponse?.user?.settings?.language ?: "en"
def notify = apiResponse?.user?.settings?.notifications ?: true
def meta = apiResponse?.metadata ?: [version: "1.0"]
println "User: ${userName}"
println "Email: ${userEmail}"
println "Bio: ${userBio}"
println "Theme: ${theme}"
println "Language: ${lang}"
println "Notifications: ${notify}"
println "Metadata: ${meta}"
Output
User: Alice Email: not provided Bio: No bio yet Theme: dark Language: en Notifications: true Metadata: [version:1.0]
Use Case 2: Build Script Configuration (Gradle/Jenkins)
Groovy-based build tools like Gradle and Jenkins pipelines use the Elvis operator constantly for environment-based configuration.
Real-World: Build Script Configuration
// Simulating build script environment resolution
// (In real Gradle/Jenkins, these come from System.getenv())
def env = [
BUILD_NUMBER: "142",
DEPLOY_TARGET: null,
LOG_LEVEL: "",
MAX_WORKERS: null
]
// Resolve configuration with fallback chain
def buildNumber = env.BUILD_NUMBER ?: "local"
def deployTarget = env.DEPLOY_TARGET ?: "development"
def logLevel = env.LOG_LEVEL ?: "INFO"
def maxWorkers = env.MAX_WORKERS ?: "4"
println "Build: #${buildNumber}"
println "Deploy to: ${deployTarget}"
println "Log level: ${logLevel}"
println "Workers: ${maxWorkers}"
// Pattern commonly seen in Jenkinsfiles
def branch = env.BRANCH_NAME ?: "main"
def registry = env.DOCKER_REGISTRY ?: "docker.io"
def tag = env.IMAGE_TAG ?: env.BUILD_NUMBER ?: "latest"
println "\nDocker image: ${registry}/myapp:${tag}"
println "Branch: ${branch}"
Output
Build: #142 Deploy to: development Log level: INFO Workers: 4 Docker image: docker.io/myapp:142 Branch: main
If you work with Gradle or Jenkins, the Elvis operator is one of the first Groovy features you should learn. It turns verbose environment variable checks into clean one-liners.
Edge Cases and Best Practices
Common Pitfalls to Avoid
- Zero as valid value:
0 ?: 10returns10, not0. Usex != null ? x : 10when zero is valid. - False as valid value:
false ?: truereturnstrue. Use explicit null check for booleans. - Empty string ambiguity: Decide early whether empty string means “no value” or “intentionally blank” in your domain.
- Side effects in left expression: The left side is evaluated once, but if it has side effects (like incrementing a counter), be sure that is intentional.
- Overusing chained Elvis: More than 3-4 chained
?:operators becomes hard to read. Extract to a method.
Best Practices Summary
- Use Elvis for strings, objects, and collections where null and empty both mean “no value”
- Use explicit null checks for numbers and booleans where zero and false are valid
- Combine
?.(safe navigation) with?:(Elvis) for deep property access with defaults - Use Elvis assignment (
?=) for lazy initialization of mutable fields - Keep chained Elvis operators to 3 levels maximum for readability
Conclusion
The Groovy Elvis operator (?:) is one of those features that makes Groovy such a pleasure to work with. From providing simple default values for null variables to chaining fallback sources, from cleaning up collections to powering build script configurations, it handles the tedious null-checking work so you can focus on the actual logic.
The bottom line: use the Elvis operator for strings, objects, and collections where “falsy means no value.” For numbers and booleans, use explicit null checks. And when you need to drill through nested objects, pair it with the safe navigation operator for a truly null-safe chain.
Summary
x ?: yreturnsxif truthy, otherwisey- Falsy values include null, false, 0, empty strings, and empty collections
- Elvis evaluates the left expression only once (unlike the manual ternary pattern)
- Chain multiple Elvis operators for fallback chains:
a ?: b ?: c - Elvis assignment (
?=) modifies the variable in place (Groovy 3+) - Do not use Elvis when zero or false are valid values in your domain
If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.
Up next: Groovy Safe Navigation Operator (?.) – Avoid NullPointerException
Frequently Asked Questions
What is the Elvis operator in Groovy?
The Elvis operator (?:) is a shorthand for the ternary operator in Groovy. The expression x ?: y returns x if x is truthy (not null, not false, not zero, not empty), otherwise it returns y. It is named after Elvis Presley because the ?: symbol resembles his hairstyle when viewed sideways.
What is the difference between the Elvis operator and the ternary operator in Groovy?
The ternary operator (condition ? a : b) evaluates a condition and returns one of two different values. The Elvis operator (x ?: y) is a shortened ternary where the condition and the true-branch are the same expression — it returns x if truthy, otherwise y. The Elvis operator also evaluates the left side only once, making it more efficient for method calls.
Does the Groovy Elvis operator work with zero and false?
Yes, but it treats them as falsy. This means 0 ?: 10 returns 10, and false ?: true returns true. If zero or false are valid values in your domain, use an explicit null check instead: x != null ? x : defaultValue. The Elvis operator is best for strings, objects, and collections.
What is the Elvis assignment operator (?=) in Groovy?
The Elvis assignment operator (?=), introduced in Groovy 3, assigns a value to a variable only if that variable is currently null or falsy. For example, name ?= ‘Guest’ sets name to ‘Guest’ only if name is null, empty, false, or zero. Unlike the regular Elvis operator, ?= actually modifies the variable.
Can I chain multiple Elvis operators in Groovy?
Yes, you can chain Elvis operators like a ?: b ?: c ?: d. Groovy evaluates left to right and returns the first truthy value. If all values are falsy, the last value (d) is returned. This is useful for fallback chains like resolving a display name from nickname, full name, email, or a default. Keep chains to 3-4 levels for readability.
Related Posts
Previous in Series: Groovy Trampoline – Recursive Closures
Next in Series: Groovy Safe Navigation Operator (?.) – Avoid NullPointerException
Related Topics You Might Like:
- Groovy Safe Navigation Operator (?.) – Avoid NullPointerException
- Groovy Spread Operator (*.) – Apply to All Elements
- Groovy Spaceship Operator (<=>) – Compare Anything
This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

No comment