Records as Value Objects
How records generate equals, hashCode and toString, and where validation goes.
Open this lesson in the learning hubKey points
- A record lists its components; Java writes the constructor, accessors,
equals,hashCodeand toString. - Equality is by value: two Money with the same components are equal, so they work as one key in a map or set.
- The compact constructor
Money { ... }is where you validate or normalise, before the fields are assigned. - Records are implicitly final and their fields are final, but a component holding a
Listis still mutable inside. - You can add your own methods, implement interfaces, and override any generated method that needs different behaviour.
- Use a record when the identity of the object IS its data. Use a class when it has a lifecycle or hidden state.
Example
import java.util.List;
import java.util.Map;
public class Main {
record Money(String currency, long cents) implements Comparable<Money> {
Money { // compact constructor: guard here
if (cents < 0) throw new IllegalArgumentException("cents must be >= 0");
currency = currency.toUpperCase(); // normalising is allowed
}
Money plus(Money other) { // return a NEW value, never mutate
return new Money(currency, cents + other.cents);
}
@Override public int compareTo(Money o) { return Long.compare(cents, o.cents); }
}
public static void main(String[] args) {
Money a = new Money("usd", 1250);
Money b = new Money("USD", 1250);
System.out.println(a); // generated toString
System.out.println("equal? " + a.equals(b)); // generated equals, by value
System.out.println("in map? " + Map.of(a, "invoice-1").containsKey(b));
System.out.println("sum " + a.plus(b));
System.out.println("sorted " + List.of(a, new Money("USD", 300)).stream().sorted().toList());
try {
new Money("USD", -1);
} catch (IllegalArgumentException e) {
System.out.println("rejected: " + e.getMessage());
}
}
}
If the data is the identity, make it a record and validate in the compact constructor.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OOP course, and every lesson in it is listed on the OOP contents page.