Core Java

Looking at Java Records

JEP 359, available as preview feature in JDK 14, introduces records to Java. Records are an easy way to model plain data aggregates.

A simple Range record looks like this:

1
record Range(int from, int to) {}

A record definition is literally the same as a final class with:

  • immutable fields
  • public accessors
  • a constructor
  • implementations for equals(), hashCode() and toString()

So we can use our record like this:

1
2
3
4
5
6
Range range = new Range(15);
 
int from = range.from(); // 1
int to = range.to(); // 5
String toString = range.toString(); // Range[from=1, to=5]
boolean equals = range.equals(new Range(15)); // true

Note that the accessors are named from() and to() instead of getFrom() and getTo().

What about constructors?

Assume we want to add a constructor to our Record to perform some validation:

1
2
3
4
5
6
7
8
9
record Range(int from, int to) {
    public Range(int from, int to) {
        if (from > to) {
            throw new IllegalArgumentException();
        }
        this.from = from;
        this.to = to;
    }
}

This avoids the creation of invalid Range instances. However, it is a bit annoying that we have to write down the from and to fields multiple times to perform a simple validation.

To avoid this, we can use a special form of constructors for records, called compact constructors. This allows us to skip defining constructor parameters and assigning constructor parameters to fields. It looks like this:

1
2
3
4
5
6
7
record Range(int from, int to) {
    public Range {
        if (from > to) {
            throw new IllegalArgumentException();
        }
    }
}

The result works exactly the same as the previous constructor.

Custom methods

We can also add new methods and override existing methods in records.

For example:

01
02
03
04
05
06
07
08
09
10
11
12
record Range(int from, int to) {
 
    public int getDistance() {
        return to - from;
    }
 
    @Override
    public String toString() {
        return String.format("Range[from: %s, to: %s, distance: %s]",
                from, to, getDistance());
    }
}

Why are records useful?

Records simply reduce the amount of code we have to write if we need a simple class to pass data around. Example use cases are multiple return values from a method, compound map keys or data transfer objects.

Assume you want to find the minimum and maximum value in a collection. With a record you can create a return type for two values with just one line:

1
2
3
record MinMax(int min, int max) {}
 
static MinMax minMax(Collection<Integer> numbers) { ... }

(Yes, you can use separate methods to find the minimum and maximum values. However, then you have to iterate the collection twice)

Records also provide an easy way to create compound Map keys:

1
2
3
record NameAndDayOfBirth(String name, LocalDate dob) {}
 
private Map<NameAndDayOfBirth, Person> entries = ...;

Summary

Records provide a less verbose way to create simple data holders. Common use cases are multiple return values, compound map keys or data transfer objects. For more background on records I recommend this writing by Brian Goetz.

You can find the example code on GitHub.

Published on Java Code Geeks with permission by Michael Scharhag, partner at our JCG program. See the original article here: Looking at Java Records

Opinions expressed by Java Code Geeks contributors are their own.

Michael Scharhag

Michael Scharhag is a Java Developer, Blogger and technology enthusiast. Particularly interested in Java related technologies including Java EE, Spring, Groovy and Grails.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button