Groovy Records
last modified March 20, 2025
Groovy 4 introduced records, a concise way to create immutable data
classes. Before records, Groovy offered @Immutable and
@Canonical annotations for similar purposes.
Simple Record Example
Here's a basic record in Groovy. Save it as SimpleRecord.groovy.
record User(String fname, String lname, String occupation) { }
def u = new User('John', 'Doe', 'gardener')
println u
println u.fname
println u.lname
println u.occupation
Records automatically provide a constructor, getters, and a toString
method. This example creates a User record and accesses its fields.
$ groovy SimpleRecord.groovy User(fname=John, lname=Doe, occupation=gardener) John Doe gardener
Named-Argument Constructor
Records support named arguments for flexible instantiation.
record User(String fname, String lname, String occupation) { }
def u = new User(lname:'Roe', fname:'Roger', occupation:'driver')
println u
Using named arguments, you can specify fields in any order. This improves readability and flexibility when creating record instances.
$ groovy NamedArgsRecord.groovy User(fname=Roger, lname=Roe, occupation=driver)
Destructuring Records
Groovy allows destructuring records into individual variables.
record User(String fname, String lname, String occupation) { }
def u = new User('John', 'Doe', 'gardener')
def (fname, lname, occupation) = u
println "${fname} ${lname} is a ${occupation}"
Destructuring assigns record fields to variables in one line. Here, we extract and use the fields in a formatted string.
$ groovy DestructureRecord.groovy John Doe is a gardener
Sortable Records
Records can be made sortable with the @Sortable annotation.
import groovy.transform.Sortable
@Sortable(includes='lname')
record User(String fname, String lname, String occupation) {}
def users = [
new User('John', 'Doe', 'gardener'),
new User('Roger', 'Roe', 'driver'),
new User('Lucia', 'Smith', 'accountant'),
new User('Paul', 'Newman', 'firefighter'),
new User('Adam', 'Clapton', 'teacher'),
new User('Jane', 'Walter', 'pilot')
]
for (def user in users) {
println user
}
println '----------------------'
users.sort()
for (def user in users) {
println user
}
The @Sortable annotation makes records comparable. Here, we
sort users by lname, showing them before and after sorting.
$ groovy SortableRecord.groovy User(fname=John, lname=Doe, occupation=gardener) User(fname=Roger, lname=Roe, occupation=driver) User(fname=Lucia, lname=Smith, occupation=accountant) User(fname=Paul, lname=Newman, occupation=firefighter) User(fname=Adam, lname=Clapton, occupation=teacher) User(fname=Jane, lname=Walter, occupation=pilot) ---------------------- User(fname=Adam, lname=Clapton, occupation=teacher) User(fname=John, lname=Doe, occupation=gardener) User(fname=Paul, lname=Newman, occupation=firefighter) User(fname=Roger, lname=Roe, occupation=driver) User(fname=Lucia, lname=Smith, occupation=accountant) User(fname=Jane, lname=Walter, occupation=pilot)
Grouping Records
Records can be grouped using Groovy's groupBy method.
import java.time.LocalDate
record User(String name, String occupation, LocalDate dob) { }
def users = [
new User('John Doe', 'gardener', LocalDate.parse('1973-09-07')),
new User('Roger Roe', 'driver', LocalDate.parse('1963-03-30')),
new User('Kim Smith', 'teacher', LocalDate.parse('1980-05-12')),
new User('Joe Nigel', 'artist', LocalDate.parse('1983-03-30')),
new User('Liam Strong', 'teacher', LocalDate.parse('2009-03-06')),
new User('Robert Young', 'gardener', LocalDate.parse('1978-11-16')),
new User('Liam Strong', 'teacher', LocalDate.parse('1986-10-23'))
]
def res = users.groupBy({ it.occupation })
for (def e in res) {
println e
}
This example groups users by occupation. The groupBy method
returns a map where keys are occupations and values are lists of users.
$ groovy GroupRecord.groovy gardener=[User(name=John Doe, occupation=gardener, dob=1973-09-07), User(name=Robert Young, occupation=gardener, dob=1978-11-16)] driver=[User(name=Roger Roe, occupation=driver, dob=1963-03-30)] teacher=[User(name=Kim Smith, occupation=teacher, dob=1980-05-12), User(name=Liam Strong, occupation=teacher, dob=2009-03-06), User(name=Liam Strong, occupation=teacher, dob=1986-10-23)] artist=[User(name=Joe Nigel, occupation=artist, dob=1983-03-30)]
Custom Static Factory with Records
Records can include static methods, like this example splitting users by millennial status.
import java.time.LocalDate
record User(String name, String occupation, LocalDate dob) {
static User of(String name, String occupation, LocalDate dob) {
return new User(name, occupation, dob)
}
}
def users = [
User.of('John Doe', 'gardener', LocalDate.parse('1973-09-07')),
User.of('Roger Roe', 'driver', LocalDate.parse('1963-03-30')),
User.of('Kim Smith', 'teacher', LocalDate.parse('1980-05-12')),
User.of('Joe Nigel', 'artist', LocalDate.parse('1983-03-30')),
User.of('Liam Strong', 'teacher', LocalDate.parse('2009-03-06')),
User.of('Robert Young', 'gardener', LocalDate.parse('1978-11-16')),
User.of('Liam Strong', 'teacher', LocalDate.parse('1986-10-23'))
]
def millen = LocalDate.parse('2000-01-01')
def res = users.groupBy({ it.dob > millen })
println 'millennials'
for (def e in res[true]) {
println e
}
println 'others'
for (def e in res[false]) {
println e
}
The static of method provides an alternative constructor. We
use it to group users born after 2000 (millennials) versus others.
$ groovy StaticFactoryRecord.groovy millennials User(name=Liam Strong, occupation=teacher, dob=2009-03-06) others User(name=John Doe, occupation=gardener, dob=1973-09-07) User(name=Roger Roe, occupation=driver, dob=1963-03-30) User(name=Kim Smith, occupation=teacher, dob=1980-05-12) User(name=Joe Nigel, occupation=artist, dob=1983-03-30) User(name=Robert Young, occupation=gardener, dob=1978-11-16) User(name=Liam Strong, occupation=teacher, dob=1986-10-23)
Source
This tutorial covered the essentials of Groovy records with examples.
Author
List all Groovy tutorials.