Java Records
Many classes only carry data: a point with an x and a y, a book with a title and an author, a result with a name and a score. Writing them in full means a constructor, private final fields, getters, a toString, and the equals and hashCode methods that collections need. A record (Java 16 and later) gives you all of that in one line.
A record in one line
record Point(int x, int y) {
}
public class Main {
public static void main(String[] args) {
Point p = new Point(3, 4);
System.out.println(p);
System.out.println(p.x() + p.y());
Point q = new Point(3, 4);
System.out.println(p.equals(q));
System.out.println(p == q);
}
}Point[x=3, y=4] 7 true false
From record Point(int x, int y) Java creates:
- private final fields
xandy, which can never change, - a constructor that takes both values,
- accessor methods
x()andy()(without thegetprefix), - a readable
toString, andequalsandhashCodethat compare the values.
Two records with the same values are equals, even though they are different objects (== is false). That is what makes records safe to use as keys in a HashMap.
Checking values in the constructor
A record can have a compact constructor: a constructor without a parameter list that runs before the fields are set. It is the place to check the values, the idea you met in Encapsulation:
record Temperature(double celsius) {
Temperature {
if (celsius < -273.15) {
throw new IllegalArgumentException("Below absolute zero: " + celsius);
}
}
}
public class Main {
public static void main(String[] args) {
System.out.println(new Temperature(21.5));
try {
new Temperature(-300);
} catch (IllegalArgumentException e) {
System.out.println("Refused: " + e.getMessage());
}
}
}Temperature[celsius=21.5] Refused: Below absolute zero: -300.0
throw and try ... catch are explained in Exceptions and Throw and Throws.
Methods in a record
A record can have its own methods and static methods. Because the fields never change, a method that "changes" something returns a new record, just like the string methods do:
record Money(long paise) {
static Money rupees(double r) {
return new Money(Math.round(r * 100));
}
Money plus(Money other) {
return new Money(paise + other.paise);
}
@Override
public String toString() {
return String.format("Rs %d.%02d", paise / 100, paise % 100);
}
}
public class Main {
public static void main(String[] args) {
Money price = Money.rupees(149.50);
Money tax = Money.rupees(7.48);
System.out.println(price.plus(tax));
}
}Rs 156.98
Records with arrays of data
Records work nicely with arrays and loops when you process a set of results:
record Result(String name, int score) {
boolean passed() {
return score >= 40;
}
}
public class Main {
public static void main(String[] args) {
Result[] results = {new Result("Aman", 72), new Result("Bina", 35), new Result("Chetan", 58)};
for (Result r : results) {
System.out.println(r.name() + (r.passed() ? " passed" : " failed"));
}
}
}Aman passed Bina failed Chetan passed
Use a record whenever a class is "just data that does not change". Use a normal class when the object must change over time or needs to hide how it stores its data.
Practice exercises
- Write a record
Book(String title, String author, int year)and print two books. - Add a compact constructor that refuses a negative year.
- Add a method
age()that returns how many years ago the book appeared (use the year 2025).