Anatomy of a Groovy file
Groovy is an agile, dynamic language for the Java Platform with features like closures, optional typing, and scripting capabilities.
File extensions: .groovy
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a Groovy file
Package declaration
Defines the namespace for the file.
A package statement, when present, is conventionally the first line of the file (comments aside). As with Java, it should match the directory the file lives in, though Groovy will not stop you from breaking that convention. It is far more forgiving about ceremony in general. Because Groovy compiles to JVM bytecode, packages interoperate directly with Java's package system: a Groovy class and a Java class in the same package see each other with no translation layer, no wrapper, and no import required.
Import statement
Brings a class or function into scope.
import java.time.LocalDate works exactly as it does in Java. Groovy runs on the JVM and can import any class from the Java standard library or a third-party JAR with no adapter code. A handful of packages (java.lang, java.util, java.io, groovy.lang, and a few others) are imported automatically, so common types are already in scope. Groovy also supports static imports (import static Math.PI) and wildcard imports (import java.util.*), letting a file pull in exactly the surface area it needs.
Class definition
A blueprint for objects, declared with class.
class declares a new type much like Java, but with less boilerplate: Groovy auto-generates a public no-arg constructor (when none is written), getters and setters for every property, and a sensible toString() unless you override them yourself. Fields default to public unless you add an explicit modifier. Groovy classes compile to ordinary .class files, so a Groovy class can extend a Java class, implement a Java interface, or be extended by Java code in the same project: the two languages coexist in the same build without friction.
Field (property)
Data stored in an object, optionally typed.
A field declared with an explicit type, like String name, behaves as it would in Java, except Groovy also generates the accessor methods automatically. person.name reads through the generated getter even though you never wrote one. A field declared with def instead, like def age = 30, is dynamically typed: its declared type is effectively Object, and it can hold anything at runtime. Because Groovy treats fields as properties by default, person.name = "Bob" and person.setName("Bob") are interchangeable. The dot-assignment form is just syntax sugar over the generated setter.
Method definition
A reusable block of code, with optional parameter and return types.
Methods may declare explicit types (String greet(String greeting = "Hello")) or use def to leave the return type dynamic. Parameters can carry default values directly in the signature, so callers may omit trailing arguments entirely, no overloads required. Groovy also makes the return keyword optional: a method's last expression is returned implicitly, a small convenience that shows up constantly once you get used to leaving it off.
String interpolation (GString)
Embeds expressions in strings using ${}.
A double-quoted Groovy string is actually a GString, not a plain java.lang.String. It lazily evaluates any ${expression} (or bare $name) placeholders it contains when the string is used. Single-quoted strings, by contrast, are always plain Java strings with no interpolation at all, which is the detail that trips up nearly everyone coming from another scripting language. Triple-quoted strings ("""...""") support the same interpolation across multiple lines, making them a natural fit for templated text blocks, SQL snippets, or multi-line log messages.
Variable declaration (def)
Declares a variable with dynamic typing.
def declares a variable without committing to a static type. The runtime type is whatever value is currently assigned, and it can change later. This is Groovy leaning into its scripting roots: quick to write, forgiving to refactor, and popular for exactly that reason in Gradle build files and Jenkins pipelines. Static typing remains available side by side: writing LocalDate today = LocalDate.now() instead of def gets compile-time checks, and @TypeChecked or @CompileStatic on a class or method escalates that checking (and performance) closer to Java's.
Closure
An anonymous function that can be assigned to a variable.
A closure ({ int currentAge -> today.year - currentAge }) is a block of code treated as a first-class value: it can be stored in a variable, passed as an argument, and invoked later with () or .call(). Closures capture variables from their enclosing scope by reference, so they can read and modify state defined outside themselves. Closures are Groovy's workhorse for collection methods (list.each { println it }, list.collect { it * 2 }) and for DSLs like Gradle's build scripts, where nearly every configuration block is secretly a closure being handed to a method.
Object instantiation
Creating a new instance, often with named parameters.
new Person(name: 'Alice', age: 25) uses Groovy's named-argument constructor: because a no-arg constructor is auto-generated, the map-like name: value pairs are applied as property assignments immediately after construction, in whatever order you list them. This sidesteps writing a matching constructor overload for every combination of fields you might want to set. A conventional positional constructor still works too if the class defines one explicitly, and both styles can coexist on the same class.
Data structures (Map/List)
Literals for maps ([k:v]) and lists ([...]).
Groovy gives both core collections first-class literal syntax: ['apple', 'banana', 'cherry'] is a List (actually an ArrayList by default), and [env: 'dev', version: 1.2] is a Map (a LinkedHashMap, preserving insertion order). Bracket access, config['env'] or the shorthand config.env, works on both. These literals lean on Groovy's broader collection API: methods like .each, .collect, .find, and .sort are added onto every List and Map, so filtering or transforming a collection rarely needs an explicit loop.
Control flow (loop & conditional)
Executes code based on logic (for, if).
A Groovy for (item in collection) iterates any iterable, list, map, or range without indices, and reads almost identically to a Python for. if/else behaves as in Java but with Groovy truth: empty collections, empty strings, and null are all falsy, not just false itself. String methods like .startsWith() come from the underlying Java String class untouched, since GStrings and plain strings both ultimately implement CharSequence. Groovy adds convenience on top of Java rather than replacing it.
Official Groovy site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.