Java 14 introduced a new way to create classes called Records. In this tutorial, we will learn:
Recommended Reading: Java 14 Features
One of the common complaints with Java has been its verbosity. If you have to create a simple POJO class, it requires the following boiler-plate code.
This verbosity is one of the reasons for high interest in Kotlin and Project Lombok.
In fact, the sheer frustration of writing these generic methods each and every time lead to the shortcuts to create them in Java IDEs such as Eclipse and IntelliJ IDEA.
Here is the screenshot showing Eclipse IDE option to generate the ceremonial methods for a class.
Java Records are meant to remove this verbosity by providing a compact structure to create the POJO classes.
Java Records is a preview feature, which is developed under JEP 359. So, you need two things to create Records in your Java projects.
You can enable Java 14 preview features in the command line using the --enable-preview -source 14
option.
Let’s say I want to create a Employee model class. It will look something like the following code.
package com.journaldev.java14;
import java.util.Map;
public class Employee {
private int id;
private String name;
private long salary;
private Map<String, String> addresses;
public Employee(int id, String name, long salary, Map<String, String> addresses) {
super();
this.id = id;
this.name = name;
this.salary = salary;
this.addresses = addresses;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public long getSalary() {
return salary;
}
public Map<String, String> getAddresses() {
return addresses;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((addresses == null) ? 0 : addresses.hashCode());
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (int) (salary ^ (salary >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (addresses == null) {
if (other.addresses != null)
return false;
} else if (!addresses.equals(other.addresses))
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (salary != other.salary)
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + ", addresses=" + addresses + "]";
}
}
Phew, that’s 70+ lines of auto-generated code. Now let’s see how to create an Employee Record class, which essentially provides the same features.
package com.journaldev.java14;
import java.util.Map;
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
}
Wow, this can’t go any shorter than this. I am already loving Record classes.
Now, let’s use the javap
command to figure out what is happening behind the scene when a Record is compiled.
# javac --enable-preview -source 14 EmpRecord.java
Note: EmpRecord.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
# javap EmpRecord
Compiled from "EmpRecord.java"
public final class EmpRecord extends java.lang.Record {
public EmpRecord(int, java.lang.String, long, java.util.Map<java.lang.String, java.lang.String>);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int id();
public java.lang.String name();
public long salary();
public java.util.Map<java.lang.String, java.lang.String> addresses();
}
#
If you want more internal details, run the javap command with -v option.
# javap -v EmpRecord
java.lang.Record
class.Let’s look at a simple example of using our EmpRecord class.
package com.journaldev.java14;
public class RecordTest {
public static void main(String[] args) {
EmpRecord empRecord1 = new EmpRecord(10, "Pankaj", 10000, null);
EmpRecord empRecord2 = new EmpRecord(10, "Pankaj", 10000, null);
// toString()
System.out.println(empRecord1);
// accessing fields
System.out.println("Name: "+empRecord1.name());
System.out.println("ID: "+empRecord1.id());
// equals()
System.out.println(empRecord1.equals(empRecord2));
// hashCode()
System.out.println(empRecord1 == empRecord2);
}
}
Output:
EmpRecord[id=10, name=Pankaj, salary=10000, addresses=null]
Name: Pankaj
ID: 10
true
false
The Record object works in the same way as any model class, data object, etc.
Sometimes, we want to have some validations or logging in our constructor. For example, employee id and salary should not be negative. The default constructor won’t have this validation. We can create a compact constructor in the record class. The code of this constructor will be placed at the start of the auto-generated constructor.
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
public EmpRecord {
if (id < 0)
throw new IllegalArgumentException("employee id can't be negative");
if (salary < 0)
throw new IllegalArgumentException("employee salary can't be negative");
}
}
If we create an EmpRecord like the following code:
EmpRecord empRecord1 = new EmpRecord(-10, "Pankaj", 10000, null);
We will get runtime exception as:
Exception in thread "main" java.lang.IllegalArgumentException: employee id can't be negative
at com.journaldev.java14.EmpRecord.<init>(EmpRecord.java:9)
Yes, we can create method in records.
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
public int getAddressCount() {
if (this.addresses != null)
return this.addresses().size();
else
return 0;
}
}
But, records are meant to be data carriers. We should avoid having utility methods in a record class. For example, the above method can be created in a utility class.
If you think that having a method is must for your Record class, think carefully if you really need a Record class?
Java Records are a welcome addition to the core programming features. You can think of it as a “named tuple”. It’s meant to create a data carrier object with compact structure, avoiding all the boiler-plate code.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.