Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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
4

The 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]
true

Records 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: -1

When 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); }.

Thanks for reading!

Donate to the site — we work hard for you.