Core Syntax Cheat Sheet
Groovy trims a lot of Java's ceremony: semicolons are optional, 'def' can replace an explicit type when you don't need strict typing, and getters/setters are auto-generated for properties declared without explicit access modifiers. Strings come in two flavors - single-quoted strings behave like Java's String, while double-quoted GStrings support interpolation, e.g., "Total: ${price * qty}", evaluating any expression inside the ${} braces.
Cricket analogy: Groovy's optional semicolons are like an experienced umpire who doesn't need a signal announced after every single delivery to know play has stopped - the boundary of a statement is understood from context, the same way seasoned officials read the game.
Closures and Control Flow Reference
A closure is written as '{ params -> body }', e.g., 'def square = { int n -> n * n }', and calling it uses parentheses like a normal method: 'square(5)'. Collections get first-class functional methods - 'list.each { println it }' iterates, 'list.collect { it * 2 }' transforms, 'list.find { it > 10 }' returns the first match, and 'list.findAll { it % 2 == 0 }' filters - all using the implicit parameter 'it' when a closure takes exactly one argument and you don't name it.
Cricket analogy: Using 'list.find { it > 10 }' to get the first match is like a selector scanning a list of net bowlers and stopping at the very first one who clocks over 140 km/h, rather than reviewing every remaining bowler in the squad.
Operators and Ranges Reference
Groovy adds several operators beyond Java: the safe navigation operator '?.' avoids NullPointerExceptions on chained calls, the Elvis operator '?:' supplies a default for null or falsy values, the spread operator '*.' calls a method or gets a property across every element of a collection (e.g., 'people*.name'), and the spaceship operator '<=>' returns -1, 0, or 1 for use in custom 'compareTo' implementations. Ranges like '1..5' (inclusive) and '1..<5' (exclusive of 5) provide a concise way to express loops and slices without manually constructing an index-based for-loop.
Cricket analogy: The spread operator 'people*.name' pulling every name from a list is like a scorecard automatically listing every player's name from the full squad sheet in one shot, instead of a scorer writing each name out individually.
// Core syntax
def name = 'Ana' // no semicolon needed
def total = "Total: ${10 * 3}" // GString interpolation -> "Total: 30"
// Closures
def square = { int n -> n * n }
println square(5) // 25
def nums = [1, 2, 3, 4, 5, 6]
nums.each { println it } // iterate, implicit 'it'
println nums.collect { it * 2 } // [2, 4, 6, 8, 10, 12]
println nums.find { it > 4 } // 5
println nums.findAll { it % 2 == 0 } // [2, 4, 6]
// Operators
def city = user?.address?.city ?: 'Unknown' // safe navigation + Elvis
def names = people*.name // spread operator
println(5 <=> 10) // -1 (spaceship operator)
// Ranges
(1..5).each { println it } // 1,2,3,4,5
(1..<5).each { println it } // 1,2,3,4Quick truthiness reference: null, an empty String (''), an empty List/Map ([]), and the number 0 are all falsy in a Groovy boolean context, so 'if (myList)' is a concise, idiomatic way to check for a non-empty collection without calling .isEmpty().
Relying heavily on dynamic typing ('def' everywhere, no @CompileStatic) in a published library's public API leaves consumers with poor IDE autocomplete and pushes type errors to their runtime instead of their compiler - for shared library code, prefer explicit types or static compilation at the public boundary.
- def declares a variable or parameter with dynamic/inferred typing; explicit types are still allowed and recommended for public APIs.
- GStrings (double-quoted) support ${} interpolation; single-quoted strings behave like plain Java Strings.
- Closures use { params -> body } syntax, with 'it' as the implicit single parameter when unnamed.
- each, collect, find, findAll, and inject are the core functional methods for iterating and transforming collections.
- ?. (safe navigation), ?: (Elvis), *. (spread), and <=> (spaceship) are Groovy operators with no direct one-symbol Java equivalent.
- Ranges use 1..5 for inclusive and 1..<5 for exclusive of the upper bound.
Practice what you learned
1. What is the implicit parameter name available in a single-argument Groovy closure if you don't name it?
2. What does the Elvis operator '?:' do?
3. What does the range '1..<5' produce compared to '1..5'?
4. What does the spread operator '*.' do when used like 'people*.name'?
5. Which operator returns -1, 0, or 1 for use in custom comparison logic?
Was this page helpful?
You May Also Like
Groovy vs Java Comparison
A practical comparison of Groovy and Java covering syntax, typing, closures, and runtime performance to help you choose the right tool and write better JVM code.
Groovy Best Practices
Practical, idiomatic guidelines for writing clean, safe, and maintainable Groovy code in real-world projects.
Groovy Interview Questions
Commonly asked Groovy interview questions and the reasoning behind good answers, covering core syntax, closures, metaprogramming, and practical scenarios.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics