Exception Handling

Groovy doesn't enforce checked exceptions. Declaring throws is optional (but useful for Java callers).

org.codehaus.groovy.runtime.StackTraceUtils.sanitize(e) removes Groovy internals from stack traces.

class InsufficientFundsException extends RuntimeException {
    BigDecimal shortfall
    InsufficientFundsException(String msg, BigDecimal shortfall) { super(msg); this.shortfall = shortfall }
}

def withdraw(balance, amount) {
    if (amount > balance) throw new InsufficientFundsException("Insufficient funds", amount - balance)
    balance - amount
}

try {
    withdraw(100, 150)
} catch (InsufficientFundsException e) {
    println "${e.message}: short by ${e.shortfall}"
} catch (IllegalArgumentException | IllegalStateException e) {   // multi-catch
    println "Bad input"
} catch (e) {                                                      // untyped catch = Exception
    println "Other: $e"
} finally {
    println "Always runs"
}

// try-with-resources (ARM blocks, Groovy 3+), or GDK withCloseable/withReader
try (def reader = new File("data.txt").newReader()) {
    println reader.readLine()
}
new File("data.txt").withReader { r -> println r.readLine() }   // auto-closes

// assert with the power assert output
def x = 5
assert x * 2 == 11 : "Math is broken"
// Assertion failed: Math is broken. Expression: (x * 2 == 11). Values: x = 5