Groovy — Reference
Source: https://groovy-lang.org/documentation.html
Groovy
- Created: 2003 by James Strachan; first stable release 1.0 in 2007; under Apache Software Foundation since 2015
- Latest stable: Groovy 5.0.x (5.0.5 series shown in current docs dropdown); Groovy 4.x is the prior stable line. (groovy-lang.org/documentation.html)
- Paradigms: Object-oriented, imperative, functional (closures), scripting; optional static typing
- Typing: Dynamic by default, static via
@CompileStatic/@TypeChecked; gradual / optional typing model - Memory: JVM-managed (whatever GC the host JVM uses)
- Compilation: Compiled to JVM bytecode by
groovyc; can run as scripts (compiled on the fly) or as.classfiles; AOT or JIT via the JVM - Primary domains: JVM scripting, build tooling (Gradle DSL — predates Kotlin DSL), test DSLs (Spock), web frameworks (Grails), CI pipelines (Jenkins shared libraries), application configuration as code
- Notable runtimes: Reference Apache Groovy on any Java 11+ JVM (Groovy 5 needs JDK 17 to build, runs on JDK 11+)
- Official docs: https://groovy-lang.org/documentation.html
At a glance
Groovy is “Java with the boring parts removed and superpowers added.” Source compiles to standard JVM bytecode; Groovy classes interop bidirectionally with Java classes (a Groovy class is a real java.lang.Class). The headline features: closures with implicit it and configurable delegate strategy, an extensive GDK that adds methods to String, Collection, File, etc., builders that lean on closure delegation to produce nested DSLs (the foundation of Gradle), runtime metaprogramming via MetaClass, and compile-time metaprogramming via AST transformations (@ToString, @EqualsAndHashCode, @CompileStatic, @Singleton, @Immutable). Groovy 5.0 added support up to JDK 25, JEP 512 compact source files, instance-main, and pattern matching for instanceof. (Groovy 5 release notes)
Getting started
Install:
- SDKMAN (canonical):
sdk install groovy, switch withsdk use groovy 5.0.5. SDKMAN is the de facto version manager. - Homebrew:
brew install groovy - Manual: download from https://groovy.apache.org/download.html, set
GROOVY_HOME, add$GROOVY_HOME/bintoPATH. Requires JDK 11+ (5.x).
Hello world:
// hello.groovy
println "Hello, world!"Run as a script (no class needed): groovy hello.groovy. Or wrap in a class:
class App {
static void main(String[] args) { println "Hello!" }
}Project layout: Driven by Gradle, the canonical build tool (which is itself written in / configured with Groovy):
src/main/groovy/com/acme/App.groovy
src/test/groovy/com/acme/AppSpec.groovy // Spock specs
build.gradle // Groovy DSL (or Kotlin DSL)
settings.gradle
Package/build tool:
- Gradle is the dominant build tool.
gradle init --type groovy-application. - Maven with
gmavenplus-pluginworks fine. - Grape (
@Grab) for inline dependency declarations in scripts:@Grab('org.apache.commons:commons-lang3:3.14.0')resolves and adds to classpath at runtime via Ivy.
REPL: groovysh — Groovy 5 ships a JLine-3 rebuild with syntax highlighting, completion, and history. (release notes) groovyConsole is a Swing GUI with an AST browser (huge for understanding what AST transforms emit).
Basics
Types & literals. (syntax doc)
- Integers:
byte,short,int,long,BigInteger. Underscore separators allowed:1_000_000. Bases:0b1010,0xFF,010(octal). - Decimals:
float,double,BigDecimalis the default for decimal literals (3.14is aBigDecimal, not adouble— a famous gotcha for Java refugees). - Strings: single-quoted
'plain'(no interpolation), double-quoted"hello $name"(GString with interpolation), triple-quoted multi-line, slashy/regex \d+/, dollar-slashy$/...$ uses different escapes/$. - Lists:
[1, 2, 3]is anArrayList. Maps:[a: 1, b: 2]is aLinkedHashMap(literal keys are auto-quoted; use[(varName): val]to use a variable as a key). - Booleans: “Groovy truth” — non-null, non-empty, non-zero, non-false-Boolean is true.
if (collection) {…}is true iff non-empty.
Variables & scoping.
def x = 42 // dynamic
int y = 42 // typed
final z = "constant"
var w = 3.14 // since Groovy 3 (Java-style var)Lexical scoping inside methods/closures. Scripts have a Binding that holds top-level vars accessible across the script.
Control flow.
if (x > 0) ... else if ... else ...
switch (val) {
case Integer: ...; break // type matching
case ~/\d+/: ...; break // regex matching
case [1, 2, 3]: ...; break // collection membership
case { it > 100 }: ...; break // closure predicate
default: ...
}
for (item in list) { ... }
list.each { item -> ... } // closure-based
list.eachWithIndex { item, i -> ... }Functions / closures.
def add(int a, int b = 0) { a + b } // last expression is the return value
Closure square = { x -> x * x }
Closure increment = { it + 1 } // implicit `it` parameter
[1,2,3].collect(square) // method-reference-likeClosures capture lexical scope and have a configurable delegate — the foundation of every Groovy DSL.
Strings. GStrings interpolate lazily — "${expensiveCall()}" is evaluated when the GString is toString()’d. == does equals() (not reference equality — another Java-refugee trap; use is() for identity). Methods like padLeft, center, * (repeat), tokenize.
Collections. GDK augments the JDK: collect, findAll, find, inject (fold), groupBy, countBy, sort, unique, sum, min/max, take/drop/takeWhile/dropWhile, *. (spread-dot — users*.name extracts every name), ?. (safe-nav), ?: (Elvis).
Intermediate
Type system depth. Three modes:
- Dynamic (default): every method call goes through MOP (
MetaClass); flexible but slower and fails at runtime. @TypeChecked: compile-time type checking with full static rules; calls still dispatched dynamically.@CompileStatic: type-checked AND statically compiled — bytecode equivalent to Java for the typed parts; ~10x faster, no metaclass tricks for those methods. Use on hot paths and library APIs.
Modules. Java-style packages (package com.acme); imports include defaults (java.lang.*, java.util.*, java.io.*, java.net.*, groovy.lang.*, groovy.util.*, java.math.BigInteger, java.math.BigDecimal). JPMS supported.
Error handling. Java-style try/catch/finally. Multi-catch catch (IOException | SQLException e). Checked exceptions exist syntactically but are not enforced by Groovy — you can throw a checked exception without declaring it. try-with-resources works on AutoCloseable.
Concurrency. All JVM concurrency primitives: Thread, ExecutorService, CompletableFuture, java.util.concurrent.*, synchronized. GPars library adds actors, dataflow, parallel collections (list.collectParallel), agent (Clojure-style). @Synchronized AST transform generates a private lock + sync block.
I/O. new File(path).text reads everything. file.eachLine { ... }, file.withReader { r -> ... } (closes for you), file << "appended\n", file.withWriter, Process extensions: "ls -la".execute().text. XmlSlurper/XmlParser for XML, JsonSlurper/JsonOutput for JSON.
Stdlib highlights. groovy.json.*, groovy.xml.*, groovy.sql.Sql (concise JDBC wrapper), groovy.text.SimpleTemplateEngine/StreamingTemplateEngine, groovy.transform.* (the AST-transform pack), groovy.util.ConfigSlurper (typed config files), groovy.cli.commons.CliBuilder/groovy.cli.picocli.CliBuilder (CLI parsing).
Advanced
Memory / GC. Whatever the JVM provides (G1 default since Java 9, ZGC for low pause). Closures retain enclosing scope — a closure stored in a long-lived collection holds references to its captured locals; classic memory leak source.
Concurrency deep dive. @CompileStatic + j.u.c.* is the production pattern. GPars Actors give Erlang-style mailbox concurrency on the JVM. Dataflow variables (def x = new DataflowVariable(); x << compute()) for declarative parallelism. Parallel array methods (list.makeConcurrent().collect{…}).
FFI. No special FFI; use the JVM’s: JNI (manual C glue), JNA (declarative), Project Panama / java.lang.foreign (since JDK 22) — all callable directly from Groovy with the same syntax as Java. Groovy is also commonly the FFI: embed a GroovyShell in a Java app to evaluate user-supplied scripts.
Reflection. Java reflection works (obj.class.methods). Groovy adds MetaClass access: String.metaClass.shout = { -> delegate.toUpperCase() }; "hi".shout() adds an instance method at runtime. obj.metaClass.getMetaMethods(), respondsTo(), hasProperty(). The ExpandoMetaClass lets you mutate any class’s MOP dynamically — testing/mocking superpower.
Performance tools. JFR / async-profiler / VisualVM all work on Groovy bytecode. Use groovyc -d build to inspect emitted bytecode with javap -c. @CompileStatic is the single biggest win — typically 5-10x for arithmetic-heavy code. Avoid GString creation in hot paths (allocates per call); pre-build String.format patterns.
God mode
-
AST Transformations. Two flavors. (1) Local annotations like
@ToString,@EqualsAndHashCode,@TupleConstructor,@Builder,@Immutable,@Memoized,@Singleton,@Sortable,@Delegate,@Lazy,@Newify— apply at compile time, generate methods/constructors. (2) Global transforms via@GroovyASTTransformationthat rewrite arbitrary AST. Build by implementingorg.codehaus.groovy.transform.ASTTransformationand registering inMETA-INF/services. Inspect what they emit using groovyConsole → Script → Inspect AST. -
@CompileStatic+@TypeChecked(extensions=...). Custom static-type-checker extensions let you build DSL-aware type checkers (e.g.,@TypeChecked(extensions = 'sql-typechecker.groovy')validates SQL string types at compile time). -
GroovyShell/GroovyClassLoader. Runtime evaluation:new GroovyShell().evaluate("1+1"). Configure withCompilerConfigurationto inject AST transforms, default imports, base script class, security policy. Pattern: an app exposes its API as aScriptbase class; user scripts get DSL methods for free. -
Closure delegate strategies. Every closure has
delegate,owner,thisObject.delegatestrategy can beOWNER_FIRST(default),DELEGATE_FIRST,OWNER_ONLY,DELEGATE_ONLY. Setclosure.resolveStrategy = Closure.DELEGATE_FIRSTand unqualified names resolve againstdelegatefirst — the trick that makestask('build') { dependsOn 'compile' }look like syntax in Gradle. -
Builder pattern (DSL fundamentals).
MarkupBuilder,JsonBuilder,SwingBuilder,XmlBuilderusemethodMissing+propertyMissing+ closure delegation:new MarkupBuilder().html { head { title 'Hi' }; body { p 'Hello' } } -
ExpandoMetaClass+ categories. Add methods at runtime to any class:Number.metaClass.squared = { delegate * delegate }; 5.squared(). Categories scope the change to auseblock:use(StringCategory) { "hi".doSomething() }. The Spock framework leans on this for Mock/Stub. -
Mixins via metaclass.
class Foo { }; Foo.metaClass.mixin(SomeMixin)injects all methods of SomeMixin into Foo’s MOP at runtime. AST-time alternative:@Mixin(SomeMixin). -
Bytecode emission.
groovyc -d out src/Foo.groovy, thenjavap -c -p out/Foo.class. Use@CompileStaticto see how close emitted code is to handwritten Java (very close). -
Grape internals.
@Grabresolves through Ivy at script start; cache lives at~/.groovy/grapes/.groovy.grape.Grape.grab([group:'…', module:'…', version:'…'])is the programmatic API.
Idioms & style
- Naming:
lowerCamelCasefor methods/vars,UpperCamelCasefor classes,UPPER_SNAKEfor constants — same as Java. defvs explicit type: prefer types in production code (especially with@CompileStatic);defis fine in scripts and tests.- Use
==for equality,is()for identity. Opposite of Java; this trips everyone. - Prefer
each/collect/findAlloverforfor clarity (and trivially parallelizable later). @Immutablefor value classes. Generates fields, equals/hashCode/toString, defensive copies — Groovy’s analog of Java records, predates them.- Builder closures over constructor args for >3 params.
- Formatter/linter: CodeNarc is the standard linter (Checkstyle-equivalent for Groovy); IntelliJ IDEA’s Groovy plugin has the only mature formatter.
- Keep dynamic code in DSL surfaces, not hot paths.
@CompileStaticeverything that’s not a DSL.
Ecosystem
- Build: Gradle (Groovy DSL is Groovy code) — Android, Spring Boot, most JVM polyglots default to it.
- Testing: Spock Framework — given/when/then BDD DSL on Groovy with AST-rewritten assertion failures that show every subexpression. Industry standard for JVM testing in many shops. JUnit 5 + Groovy also fine.
- Web framework: Grails (Rails-inspired, Spring Boot under the hood, GORM ORM with dynamic finders).
- Scripting / orchestration: Jenkins shared libraries are Groovy; CodeNarc, Geb (browser automation, Selenium wrapper).
- Data:
groovy.sql.Sqlfor JDBC, GORM (in Grails) for ORM. - Notable users: Gradle Inc., Netflix (Spinnaker uses Groovy), LinkedIn (test infrastructure), Jenkins / CloudBees (pipelines), many financial firms for trading-rule DSLs.
Gotchas
- Decimal literals are
BigDecimal, notdouble.0.1 + 0.2 == 0.3is true in Groovy — but mixing with Java doubles causes silent boxing/unboxing perf hits. ==isequals(), not==from Java. Useis()for identity. JavaScript-like.- GString vs String.
def s = "hi $name"is aGString. Map keys, hashCodes, and equals against String can surprise:["hi $name": 1].containsKey("hi bob")isfalse— the GString hashes lazily. Force with.toString(). - Checked exceptions silently bypass. Calling Java code that throws checked exceptions doesn’t require declaration — fine for scripts, dangerous in mixed Groovy/Java codebases.
- Closure capture leaks. A closure stored in a static field captures its enclosing scope; classloader leaks in app servers.
- Default parameter classes are loaded for
def. If you usedef xin@CompileStatic, you opt back into dynamic dispatch for that variable; defeats the optimization. itshadowing. Nested closures all useit; inneritshadows outer. Name parameters explicitly when nesting.- Map literal vs labelled-statement ambiguity.
[a: 1]is a map.a:at statement position can be parsed as a label. Rare but surprising. - Grape behind a corporate proxy needs
~/.groovy/grapeConfig.xmlIvy settings; a Grape failure is a script failure with a confusing stack trace. Pre-resolve with Maven or set up a Nexus proxy. - Groovy 5 needs JDK 17 to build, JDK 11 to run. Some older Java environments need an upgrade. (release notes)
@CompileStaticremoves meta-programming for that scope —someStr.metaClass.foo = …won’t take effect on@CompileStaticcallers.
Citations
- Groovy documentation hub — https://groovy-lang.org/documentation.html
- Groovy 5.0 release notes — https://groovy-lang.org/releasenotes/groovy-5.0.html
- Syntax reference — https://groovy-lang.org/syntax.html
- Closures guide — https://groovy-lang.org/closures.html
- Metaprogramming — https://groovy-lang.org/metaprogramming.html
- AST transformations — https://docs.groovy-lang.org/latest/html/documentation/core-metaprogramming.html#_compile_time_metaprogramming
- Type checking &
@CompileStatic— https://docs.groovy-lang.org/latest/html/documentation/#_static_type_checking - Grape /
@Grab— https://docs.groovy-lang.org/latest/html/documentation/grape.html - Gradle DSL reference — https://docs.gradle.org/current/dsl/
- Spock Framework — https://spockframework.org/spock/docs/