GEP-14


Metadata
Number

GEP-14

Title

Record classes

Version

1

Type

Feature

Status

Draft

Leader

Paul King

Created

2021-10-26

Last modification 

2021-10-26

Abstract

Record classes, or records for short, are a special kind of class useful for modelling plain data aggregates. They provide a compact syntax with less ceremony than normal classes. Groovy already has AST transforms such as @Immutable and @Canonical which already dramatically reduce ceremony but records have been introduced in Java and record classes in Groovy are designed to align with Java record classes.

Motivation

For motivation of the general concept for records, see the References and useful links section below. The overall summary is that very succinct classes can be written for the special case of plain data aggregates, e.g.:

record Point3D(int x, int y, int z) { }

or:

record Person(String firstName, String lastName) { }

Such classes have automatic toString, hashCode and equals methods and an automatic tuple constructor. All of these factor into account the properties (known as record components) of the class.

Requirements

The main requirement is to provide equivalent functionality to Java record classes when compiling on suitable JDK versions (16+). By equivalent functionality, the following aspects are relevant:

  • Support reduced ceremony when writing records.

  • Store appropriate information at the bytecode level so that Groovy records are recognised by Java. We refer to classes with such bytecode information as native records.

  • Maintain Java syntax compatibility where possible including the compact constructor syntax.

Non-goals

  • Provide native record support on versions of the JDK which supported records in preview mode

Design considerations

  • Numerous Groovy AST transforms already provide functionality that overlaps with some features of Java records, e.g. @ToString helps reduce ceremony by offering a declarative mechanism to achieve an automatic toString method. Where it makes sense, Groovy’s record implementation should leverage such existing functionality.

  • Even though existing Groovy AST transforms can be pieced together to achieve mimic record functionality, it sees worth providing a pre-canned packaging of those pieces to mirror record functionality in a concise way. Records should not introduce any additional impedance for Java developers learning or using Groovy.

  • Groovy’s existing AST transforms offer additional customization options and additional boilerplate reduction options. These should be available with Groovy records (when it makes sense).

  • Given that Groovy’s existing AST transforms work for JDK versions prior to JDK16, Groovy’s record functionality can allow the creation of record-like classes. Unlike native records, these won’t have record information at the bytecode level but will otherwise follow the same conventions as native records. Such classes will store record information using annotations. They will be recognised by the Groovy compiler but not a Java compiler.

  • Given that records are primarily designed to be data aggregates, Groovy records are @CompileStatic by default.

Groovy special features

  • Support Groovy’s named-parameter syntax

  • Support Groovy’s getAt method for positional access of components

  • Support Groovy’s default parameter concept

  • Support additional helper methods found in other languages, e.g. Kotlin data classes and Scala case classes. Candidates include copyWith, size, toMap, toList.

  • Support destructuring of records

Initial implementation

  • Provide a @RecordType annotation collector which collects existing AST transforms suitable for providing the desired functionality.

  • Provide a @RecordBase AST transform which encapsulates any special treatment not found in existing transforms.

  • Provide a @RecordOptions annotation which allows the constructed record implementation to be customised.

  • Provide support for the record keyword in the grammar which can be used instead of the @RecordType annotation.

Update history

1 (2021-10-26) Initial draft 2 (2021-11-06) Update to align with 4.0.0-beta-2