Records
Many classes just hold data: a point, a product, an address. For them you have to write a constructor, getters, equals(), hashCode() and toString(). A record (record) does all of this for you in one line.
The first record
After the word record you write a name and, in parentheses, the components — that is, the record's fields. Java creates the constructor and the accessor methods by itself:
record Point(int x, int y) {}
void main() {
Point p = new Point(3, 4);
IO.println(p.x());
IO.println(p.y());
}Output:
3
4The accessor methods have the same names as the components: x(), not getX().
Ready-made toString and equals
A record prints in a readable form and compares by contents. The hashCode() method is consistent with equals() as well, so records can safely go into a HashSet and serve as HashMap keys:
record Product(String title, double price) {}
void main() {
Product a = new Product("Tea", 4.5);
Product b = new Product("Tea", 4.5);
IO.println(a);
IO.println(a.equals(b));
}Output:
Product[title=Tea, price=4.5]
trueRecords are immutable
All fields of a record are private final. You cannot change the object after it is created; instead you create a new one with the values you need:
record Product(String title, double price) {
Product withPrice(double newPrice) {
return new Product(title, newPrice);
}
}
void main() {
Product tea = new Product("Tea", 4.5);
Product cheaper = tea.withPrice(3.9);
IO.println(tea);
IO.println(cheaper);
}Output:
Product[title=Tea, price=4.5]
Product[title=Tea, price=3.9]As you can see, a record can declare its own methods — just like an ordinary class.
The compact constructor
To validate the data, you write a compact constructor: no parameter list, only the record's name. You do not need to assign the fields in it — Java does that after the check:
record Age(int years) {
Age {
if (years < 0) {
throw new IllegalArgumentException("Age cannot be negative: " + years);
}
}
}
void main() {
IO.println(new Age(30));
IO.println(new Age(-1));
}Output:
Age[years=30]
Exception in thread "main" java.lang.IllegalArgumentException: Age cannot be negative: -1When to use a record
A record fits simple "data carriers": results of calculations, table rows, messages between parts of a program. Records cannot extend other classes, but they can implement interfaces. If an object needs to change its state, you need an ordinary class.
Common mistake
A record's field cannot be changed even inside its own method — it is final:
record Counter(int value) {
void increment() {
value++;
}
}
void main() {
}Output:
Main.java:3: error: cannot assign a value to final variable value
value++;
^Return a new record instead: Counter increment() { return new Counter(value + 1); }.