Java : Basics of Record class
Record classes make it concise to declare classes that have simple data structures. The record class was added as a language specification in Java 16.
This article provides the basic usage of the record class.
Note:
- This article may use translation software for your convenience. Please also check the original Japanese version.
Comparing normal classes and record classes
Normal class example
Let's think of a very simple class.
public class Book {
private final String title;
private final int totalPage;
public Book(String title, int totalPage) {
this.title = title;
this.totalPage = totalPage;
}
public String getTitle() {
return title;
}
public int getTotalPage() {
return totalPage;
}
}
The Book class has title and totalPage fields and getter methods to get them.
public class Book {
private final String title;
private final int totalPage;
...
All fields are private final. They are not allowed to modify.
final var book = new Book("TITLE!", 100);
System.out.println(book.getTitle()); // TITLE!
System.out.println(book.getTotalPage()); // 100
The Book class works just fine.
Record class example
The Book class as the normal class can be declared concisely using the record class.
public record Book(String title, int totalPage) {
}
It's very concise!
The record class uses the record keyword instead of the class keyword. And you don't need to declare constructors, fields, and getter methods.
final var book = new Book("TITLE!", 100);
System.out.println(book.title()); // TITLE!
System.out.println(book.totalPage()); // 100
You can create a record object with the new keyword. It is the same as normal classes. And you can get field values with getter methods that have the same name as the parameter names in the header(e.g. title, totalPage).
Features of Record Classes
Auto-declared fields
public record Book(String title, int totalPage) {
}
A record class has auto-declared fields. These fields are private final, with the same name and declared type as the header parameters.
This record class is equivalent to the following normal class :
public class Book {
private final String title;
private final int totalPage;
...
You can also add methods to the record class to access auto-declared fields.
public record Book(String title, int totalPage) {
public void printAll() {
System.out.println("title : " + title);
System.out.println("totalPage : " + totalPage);
}
}
final var book = new Book("TITLE!", 100);
book.printAll();
// Result
// ↓
//title : TITLE!
//totalPage : 100
Auto-declared constructor
public record Book(String title, int totalPage) {
}
A record class has an auto-declared constructor. This constructor initializes the fields with the given parameters.
This record class is equivalent to the following normal class :
public class Book {
private final String title;
private final int totalPage;
public Book(String title, int totalPage) {
this.title = title;
this.totalPage = totalPage;
}
...
If you want to validate parameters of the constructor, you can use a compact constructor.
public record Book(String title, int totalPage) {
public Book {
if (title == null || title.isEmpty()) {
throw new IllegalArgumentException("title is invalid! : " + title);
}
if (totalPage < 0) {
throw new IllegalArgumentException("totalPage is invalid! : " + totalPage);
}
}
}
try {
var _ = new Book(null, 100);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
// Result
// ↓
//title is invalid! : null
try {
var _ = new Book("TITLE!", -1);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
// Result
// ↓
//totalPage is invalid! : -1
This record class is equivalent to the following normal class :
public class Book {
private final String title;
private final int totalPage;
public Book(String title, int totalPage) {
if (title == null || title.isEmpty()) {
throw new IllegalArgumentException("title is invalid! : " + title);
}
if (totalPage < 0) {
throw new IllegalArgumentException("totalPage is invalid! : " + totalPage);
}
this.title = title;
this.totalPage = totalPage;
}
...
Auto-declared getter methods
public record Book(String title, int totalPage) {
}
final var book = new Book("TITLE!", 100);
System.out.println(book.title()); // TITLE!
System.out.println(book.totalPage()); // 100
A record class has auto-declared methods for getting field values. These method names have the same names as the header parameters of the record class.
This record class is equivalent to the following normal class :
public class Book {
...
public String title() {
return title;
}
public int totalPage() {
return totalPage;
}
...
Auto-implemented equals and hashCode methods
public record Book(String title, int totalPage) {
}
final var book1 = new Book("TITLE!", 100);
final var book2 = new Book("TITLE!", 100);
System.out.println(book1.equals(book2)); // true
System.out.println(book1.hashCode()); // -589266789
System.out.println(book2.hashCode()); // -589266789
A record class has auto-implemented equals method and hashCode method of the Object class.
The book1 and book2 objects have the same content, so the equals method returns true. The hashCode method also returns the same value. This is useful when you use record classes as Map keys.
If a normal class does not override the equals method, it returns false even if objects have the same content. The hashCode method may not return the same value.
public class Book {
private final String title;
private final int totalPage;
public Book(String title, int totalPage) {
this.title = title;
this.totalPage = totalPage;
}
}
final var book1 = new Book("TITLE!", 100);
final var book2 = new Book("TITLE!", 100);
System.out.println(book1.equals(book2)); // false
System.out.println(book1.hashCode()); // 366252104
System.out.println(book2.hashCode()); // 1889057031
Auto-implemented toString method
public record Book(String title, int totalPage) {
}
final var book = new Book("TITLE!", 100);
final var str = book.toString();
System.out.println(str); // Book[title=TITLE!, totalPage=100]
A record class has an auto-implemented toString method of the Object class. The implemented toString method returns the record class name and each field as a string. It's useful when debugging.
If a normal class does not override the toString method, it returns a simple string representation of the object.
public class Book {
private final String title;
private final int totalPage;
public Book(String title, int totalPage) {
this.title = title;
this.totalPage = totalPage;
}
}
final var book = new Book("TITLE!", 100);
final var str = book.toString();
System.out.println(str); // Book@2235eaab
Official documentation links
Record classes are introduced in the Java Language updates documentation. There are some features that are not covered in this article, it would be better to check it.
For more information, please check the Java Language Specification.
Conclusion
Personally, I often create classes with simple data structures, so record classes are a very nice feature addition. It's also nice that it automatically implements equals, hashCode, and toString methods.
Using record classes would make your code simpler and more readable. Let's make good use of it.