Java Records
A Java record is a compact way to declare a class whose main job is to carry data. A record automatically gets a constructor, accessor methods, equals, hashCode, and toString based on the fields you list.
Records are useful when you want a simple, immutable value type without writing a lot of repeated boilerplate code.
Basic Record Syntax
Create a record with the record keyword, followed by its components in parentheses. Each component becomes a private final field and gets a public accessor method with the same name.
record Student(String name, int grade) {
}
public class Main {
public static void main(String[] args) {
Student student = new Student("Mia", 92);
System.out.println(student.name());
System.out.println(student.grade());
System.out.println(student);
}
}
Output:
Mia
92
Student[name=Mia, grade=92]
The record header Student(String name, int grade) declares the data that belongs to each student. Java creates a constructor that accepts those two values, so new Student("Mia", 92) works without writing the constructor yourself.
Accessors, Not Getters
Record accessor methods do not use the usual getName() style. The accessor for name is name(), and the accessor for grade is grade(). This keeps records small and direct.
Record fields are final. After a record object is created, you cannot assign a new value to one of its components. If you need different data, create a new record object.
Generated Equality
Records compare their component values by default. This makes them useful for values such as coordinates, names, settings, and small results returned from methods.
record Point(int x, int y) {
}
public class Main {
public static void main(String[] args) {
Point first = new Point(3, 4);
Point second = new Point(3, 4);
Point third = new Point(5, 4);
System.out.println(first.equals(second));
System.out.println(first.equals(third));
System.out.println(first.hashCode() == second.hashCode());
}
}
Output:
true
false
true
The first two points are equal because both their x and y values match. The third point is different because its x value is different.
Adding Methods
A record can have methods just like a class. Add methods when they calculate something from the record’s components or make the value easier to use.
record Rectangle(int width, int height) {
public int area() {
return width * height;
}
public boolean isSquare() {
return width == height;
}
}
public class Main {
public static void main(String[] args) {
Rectangle banner = new Rectangle(8, 3);
Rectangle tile = new Rectangle(5, 5);
System.out.println("Banner area: " + banner.area());
System.out.println("Tile is square: " + tile.isSquare());
}
}
Output:
Banner area: 24
Tile is square: true
Validating Values
You can validate record data in a compact constructor. A compact constructor has the record name but no parameter list. Java still provides the parameters from the record header.
record Product(String name, double price) {
public Product {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Name is required");
}
if (price < 0) {
throw new IllegalArgumentException("Price cannot be negative");
}
}
}
public class Main {
public static void main(String[] args) {
Product book = new Product("Java Guide", 19.99);
System.out.println(book.name());
System.out.println(book.price());
}
}
Output:
Java Guide
19.99
The compact constructor checks the incoming values before the record is fully created. If a value is invalid, it throws an exception instead of creating a bad object.
Records Compared With Classes
| Feature | Record | Regular class |
|---|---|---|
| Main use | Simple data carrier | Any object design |
| Fields | Final components | Can be mutable or final |
| Generated methods | Constructor, accessors, equality, hash code, string output | You write or generate them yourself |
| Inheritance | Cannot extend another class | Can extend a class |
When To Use Records
- Use a record for small objects that mainly group related values together.
- Use a record for method return values that need more than one piece of data.
- Use a regular class when the object has changing state, complex behavior, or needs to extend another class.
- A record can implement interfaces, but it cannot extend another class because all records already extend
java.lang.Record.
Takeaway: records let you create clear, immutable data carrier types without writing repeated constructor, accessor, and equality code.
