What you'll learn

  • Groovy truth — what counts as false
  • The Elvis operator ?: for defaults
  • Groovy's super-powered switch
  • Loops — for-in, times, upto, each

Groovy’s if, while and for look like Java’s, but two features change how you write conditions: Groovy truth and a far more capable switch.

Groovy truth

In Java, a condition must be a boolean. In Groovy, any value can be a condition:

ValueCounts as
nullfalse
0, 0.0false
'' (empty string)false
[], [:] (empty list/map)false
falsefalse
anything elsetrue
def customer = ''
if (!customer) {
    println 'No customer given'
}
if (orders) { ... }          // "if there are any orders"

This replaces if (orders != null && !orders.isEmpty()) with if (orders).

Watch out: 0 is false. If zero is a legitimate value (a quantity, a balance), compare explicitly: if (qty != null).

The Elvis operator

def name = customer ?: 'Guest'

“Use customer if it’s true (by Groovy truth), otherwise 'Guest'” — the everyday way to apply defaults.

A switch that matches almost anything

def describe(value) {
    switch (value) {
        case null              -> 'nothing'
        case Integer           -> "a number: $value"              // type
        case ~/[A-Z]{2}-\d{3}/ -> "a SKU: $value"                 // regular expression
        case ['NEW', 'OPEN']   -> 'an open status'                // one of these values
        case { it instanceof List && it.size() > 2 } -> 'a long list'   // any condition
        default                -> "something else: $value"
    }
}

Groovy’s switch uses each case’s isCase method: types check instanceof, patterns check matches, collections check membership, ranges check bounds, and closures run as conditions. The arrow form (Groovy 4) returns a value with no fall-through; the classic case x: … break form works too.

Loops

for (sku in ['AX-200', 'BX-310']) println sku    // for-in
3.times { i -> println i }                        // 0, 1, 2
1.upto(3) { println it }                          // 1, 2, 3
while (n < 2) { n++ }
['a', 'b'].eachWithIndex { v, i -> println "$i: $v" }

In idiomatic Groovy, most loops become collection methods like each, collect and findAll — see Functional collection methods.

The full example

// control.groovy — if, switch, loops and "Groovy truth".

// Groovy truth: these all count as false in a condition
def falsy = [null, 0, '', [], [:], false]
falsy.each { value -> assert !value }
println 'null, 0, empty string, empty list, empty map and false are all "false"'

def customer = ''
if (!customer) {
    println 'No customer given'          // runs: empty string is false
}

// Elvis operator: value if it's "true", otherwise a default
def name = customer ?: 'Guest'
println name                             // Guest

// switch is very flexible in Groovy
def describe(value) {
    switch (value) {
        case null          -> 'nothing'
        case Integer       -> "a number: $value"
        case ~/[A-Z]{2}-\d{3}/ -> "a SKU: $value"
        case ['NEW', 'OPEN'] -> 'an open status'
        case { it instanceof List && it.size() > 2 } -> 'a long list'
        default            -> "something else: $value"
    }
}
['AX-200', 42, 'NEW', [1, 2, 3], null, 'hello'].each { println describe(it) }

// Loops
for (sku in ['AX-200', 'BX-310']) println "for-in: $sku"
3.times { i -> println "times: $i" }
1.upto(3) { println "upto: $it" }
def n = 0
while (n < 2) { println "while: ${n++}" }

Try it yourself

Write shippingFor(order) where order is a map: return 'free' for totals of 100 or more, 'express' if order.priority is set, otherwise 'standard' — and 'unknown' if the order is null.

Show a solution
def shippingFor(Map order) {
    if (!order) return 'unknown'
    if (order.total >= 100) return 'free'
    order.priority ? 'express' : 'standard'
}
assert shippingFor(null) == 'unknown'
assert shippingFor([total: 150]) == 'free'
assert shippingFor([total: 20, priority: true]) == 'express'
assert shippingFor([total: 20]) == 'standard'

Key takeaways

  • null, 0, empty strings, empty collections and false are all “false” in conditions.
  • a ?: b gives a default.
  • switch can match types, regexes, collections, ranges and closures.
  • Prefer collection methods over manual loops in idiomatic Groovy.

Example files