- {{node.title}}
{{node.type}} · {{ node.urlSource.name }} by {{node.authors[0].realName }}
Java14: Records
Java14: Records
In this article, check out a new feature of Java 14, Records, and see how you can implement them as more effective "data carriers".
Mar. 11, 20 · Java Zone ·
Comment (0)
Join the DZone community and get the full member experience.
Java 14 introduces a new interesting feature: records. Here, you can find the official proposal: JEP 359 Records. The goal is to have a "data carrier" class without the traditional ‘Java ceremony’ (boilerplate).
In other words, a record represents an immutable state. Record, like Enum, is a restricted form of class. The feature has still a preview status and it could change in future releases.
Use Cases
Typical use cases are: DTOs, compound keys, data structures used as tuples (e.g. Pair, Map.Entry), method’s multiple return, tree nodes.
Records are not intended to replace (mutable) data objects or libraries like Lombok.
Benefits
equals()
,hashCode()
,toString()
,constructor()
, and read accessors are generated for you.- Interfaces can be implemented.
Restrictions
- A record cannot be extended — it’s a final class.
- A record cannot extend a class.
- The value (reference) of a field is final and cannot be changed.
Record Definition
The body is optional. The state description declares the components of the record. This simple line of code is translated by the compiler in a class similar to this one:
public final class Person extends Record {
public final String name;
public final Integer yearOfBirth;
public Person(String name, Integer yearOfBirth) {
this.name = name;
this.yearOfBirth = yearOfBirth;
}
public String name() {
public final String name;
0
public final String name;
1
public final String name;
2
public final String name;
3
public final String name;
4
public final String name;
5
public final String name;
6
public final String name;
7
public final String name;
8
public final String name;
9
public final Integer yearOfBirth;
0
Beware
If the fields contain objects, only the reference is immutable. The referenced objects can change its value, compromising the state of the record. For this reason, you should use immutable objects in your record to avoid surprises.
You may also like: A Guide to Streams: In-Depth Tutorial With Examples.
Examples
How to Execute Them
To execute the examples, you can use JShell
with the flag --enable-preview
or compile your source using the flags javac --enable-preview --release 14 [source].java
and execute it using java --enable-preview [mainclass]
.
If you are using a single file program, you need the source flag: java --enable-preview --source 14 [source].java
The code in this post has been tested with JShell and IntelliJ (EAP), using OpenJDK build 14-ea+32-1423.
minimalistic
public final Integer yearOfBirth;
1
This is a minimalistic valid record.
public final Integer yearOfBirth;
2
public final Integer yearOfBirth;
3
public final Integer yearOfBirth;
4
public final Integer yearOfBirth;
5
public final Integer yearOfBirth;
6
public final Integer yearOfBirth;
7
public final Integer yearOfBirth;
8
public final Integer yearOfBirth;
9
0
1
Adding a Field
In this example, we add an argument to the new record.
2
3
Java adds the private field ( final String name;
) and the accessor ( public String name() {return this.name;}
) to the class and implements toString()
, equals()
, and the constructor Person(String name) {this.name = name}
.
4
5
6
7
8
9
public Person(String name, Integer yearOfBirth) {
0
public Person(String name, Integer yearOfBirth) {
1
public Person(String name, Integer yearOfBirth) {
2
public Person(String name, Integer yearOfBirth) {
3
public Person(String name, Integer yearOfBirth) {
4
public Person(String name, Integer yearOfBirth) {
5
public Person(String name, Integer yearOfBirth) {
6
public Person(String name, Integer yearOfBirth) {
7
public Person(String name, Integer yearOfBirth) {
8
public Person(String name, Integer yearOfBirth) {
9
this.name = name;
0
this.name = name;
1
Noteworthy here:
- the fields are private and final
- an accessor is created for the fields without the traditional bean notation ‘get’.
implementing an interface
records
can implement an interface, here an example:
this.name = name;
2
this.name = name;
3
this.name = name;
4
this.name = name;
5
this.name = name;
6
this.name = name;
7
this.name = name;
8
this.name = name;
9
this.yearOfBirth = yearOfBirth;
0
this.yearOfBirth = yearOfBirth;
1
this.yearOfBirth = yearOfBirth;
2
this.yearOfBirth = yearOfBirth;
3
this.yearOfBirth = yearOfBirth;
4
Implementing Multiple Constructors
Records implement a constructor with fields declared as parameters.
this.yearOfBirth = yearOfBirth;
5
this.yearOfBirth = yearOfBirth;
6
This code generates something like:
this.yearOfBirth = yearOfBirth;
7
this.yearOfBirth = yearOfBirth;
8
this.yearOfBirth = yearOfBirth;
9
}
0
}
1
}
2
}
3
}
4
}
5
}
6
}
7
}
8
}
9
0
1
2
3
If you try to instantiate the record without the two parameters an exception is thrown:
4
5
6
7
8
9
public String name() {
0
public String name() {
1
You can add your own constructor if needed (e.g. not all the parameters are required).
public String name() {
2
public String name() {
3
public String name() {
4
public String name() {
5
public String name() {
6
public String name() {
7
In this case, you can instantiate an object using the extra constructor:
public String name() {
8
public String name() {
9
Mutating the State
In this example, I show how it’s possible to mutate the values inside a record
. The types used in a record should be immutable to be sure that the state won’t change.
public final String name;
00
public final String name;
01
public final String name;
02
public final String name;
03
public final String name;
04
public final String name;
05
public final String name;
06
public final String name;
07
public final String name;
08
public final String name;
09
public final String name;
10
public final String name;
11
public final String name;
12
public final String name;
13
public final String name;
14
public final String name;
15
public final String name;
16
public final String name;
17
public final String name;
18
public final String name;
19
public final String name;
20
Redefining an Accessor
When you declare a record, you can redefine an accessor method. This is useful if you need to add annotations or modify standard behavior.
public final String name;
21
public final String name;
22
public final String name;
23
public final String name;
24
public final String name;
25
public final String name;
26
public final String name;
27
public final String name;
28
public final String name;
29
public final String name;
30
public final String name;
31
Annotations
Records components support annotation. The annotation requires has to declare RECORD_COMPONENT as target.
public final String name;
32
public final String name;
33
public final String name;
34
public final String name;
35
public final String name;
36
public final String name;
37
public final String name;
38
Further Reading
Like This Article? Read More From DZone
Comment (0)
Published at DZone with permission of Marco Molteni . See the original article here.
Opinions expressed by DZone contributors are their own.
Java Partner Resources
- {{ node.blurb }}
{{ editionName }}
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ message }}
{{ $dialog.title }}