days
-4
-4
hours
-1
-9
minutes
-1
-5
seconds
-5
-6
search
That's got to be a new record

JEP 359: Records

Chris Stewart
JEP 359
© Shutterstock / Alena Marchuk

A new Java enhancement proposal, JEP 359, has graduated from being a simple draft. It proposes to bring records to Java, a new kind of type declaration. Records could work in tandem with sealed types, which is the business of its younger sibling, JEP 360. Let’s take a closer look at the future of Java.

In April 2019, Java Language Architect Brian Goetz submitted a JEP draft proposing two new features be added to Java. Now, the draft is all grown up and we have two new JEPs; JEP 359 and JEP 360. In this article we will focus on JEP 359 Records (Preview).

JEP 359: Records

JEP 359 aims to extend Java by introducing a new kind of type declaration called records. Records are supposed to provide a new, compact syntax for the declaration of classes that are “transparent holders for shallowly immutable data.” Much like an enum, a record is a restricted form of class; it declares its representation and commits to an API that matches that representation. Records lack a degree of freedom that classes usually enjoy – the ability to decouple a class’s API from representation. In return, they gain a significant degree of concision. A record is ‘the state, the whole state, and nothing but the state.’

In Brian’s proposal, a record has the following form:

record Point(int x, int y) { }

It has a name, a state description, and a body. Most of the standard members are supposed to be derived mechanically, especially the representation and the protocols for construction, deconstruction, equality and display.

Furthermore:

Records, like enums, are classes. The record declaration can have most of the things class declarations can: accessibility modifiers, Javadoc, annotations, an implements clause, and type variables (though the record itself is implicitly final.) The component declarations can have annotations and accessibility modifiers (though the components themselves are implicitly private and final). The body may contain static fields, static methods, static initializers, constructors, instance methods, instance initializers, and nested types. Brian Goetz

Motivation, goals & non-goals

So as Brian wrote in the original JEP draft, Java has been criticized for being too verbose or having too much “ceremony”, and one of the reasons for that is classes that are nothing more than “plain data carriers”.  In order to write a simple data carrier class properly, you have to write a lot of low-value, repetitive, error-prone code like constructorsaccessorsequals()hashCode()toString(), etc. Therefore, this feature aims to make writing Java code that models simple data aggregates easier to write, read, and to be corrected.

However, he notes that although some may treat records as being primarily about boilerplate reduction, the feature has a more semantic goal: modeling data as data.

JEP 359 clearly states that the goal of this feature is not to “declare war on boilerplate; in particular, it is not a goal to address the problems of mutable classes using the JavaBean naming conventions.”

Now, let’s take a more detailed look at records and what they can and can’t do.

Restrictions

There are limitations to what a record can do; they can’t extend any other class, declare instance fields other than the private final fields that correspond to components of the state description. What this means is that only the state description can define the representation.

Records cannot be abstract and they are implicitly final. This, again, is to ensure that the API of a record is defined by its state description alone, and can’t be enhanced later by another class or record.

The components of a record are implicitly final, which follows the immutable by default policy applied to data aggregates.

The above restrictions aside, records act like normal classes in that they can be top level or nested, generic, can implement interfaces and are instantiated using the new keyword. A record’s body can declare static methods, static fields, static initializers, constructors, instance methods, instance initializers and nested types. A record and its components may be annotated in a state description. Nested records are implicitly static, a measure that avoids an immediately enclosing instance which would silently add state to the record.

Grammar

JEP 359 provides the following example of record grammar in action:

RecordDeclaration:
  {ClassModifier} record TypeIdentifier [TypeParameters] 
    (RecordComponents) [SuperInterfaces] [RecordBody]

RecordComponents:
  {RecordComponent {, RecordComponent}}

RecordComponent:
  {Annotation} UnannType Identifier

RecordBody:
  { {RecordBodyDeclaration} }

RecordBodyDeclaration:
  ClassBodyDeclaration
  RecordConstructorDeclaration

RecordConstructorDeclaration:
  {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName
    [Throws] ConstructorBody

Explicitly declaring members of a record

All of the members derived automatically from the state description can be explicitly declared if a developer so wishes. Goetz warns, however, that doing so carelessly can undermine the semantic invariants of records.

SEE ALSO: JEP 360: Sealed Types

Reflection API

The JEP further notes that it would add the following public methods to java.lang.Class:

  • RecordComponent[] getRecordComponents()
  • boolean isRecord()

The method getRecordComponents() returns an array of java.lang.reflect.RecordComponent objects, where java.lang.reflect.RecordComponent is a new class. The array’s elements correspond to the components of the record. More information can be extracted from RecordComponent in the array, including name, type, generic type, annotations, and its accessor method.

The method isRecord() returns true if the given class was declared as a record.

Alternatives

JEP 359 points out that records could be considered a nominal form of tuples, but that they often lead to inferior aggregates. One important aspect of the philosophy of Java is that names matter, with classes and members having meaningful names, whereas tuples and their components can have indifferent string names, which is undesirable.

Classes also support state validation through their constructors, whereas tuples don’t. And classes can have behavior related to their state, which tuples, as raw data, cannot offer.

Dependencies

As mentioned at the beginning of this article, JEP 359 and JEP 360 arrived together because they are both born of the same JEP draft. As such, due to the fact that records can work hand-in-hand with sealed types to form something called algebraic data types, JEP 360 is listed as a dependency.

For more information, check out JEP 359 in all its glory.

Author
Chris Stewart
Chris Stewart is an Online Editor for JAXenter.com. He studied French at Somerville College, Oxford before moving to Germany in 2011. He speaks too many languages, writes a blog, and dabbles in card tricks.

Leave a Reply

Be the First to Comment!

avatar
400
  Subscribe  
Notify of