The record is a special keyword in Java that was introduced in Java 14 as a preview feature and subsequently later standardized in Java 16.
The record, like the enum in Java, is a special class that may be used as a DTO (data transfer object) or as a POJO (plain old Java object).
In Java, you can use the record whenever you require the storage of data at once and then transfer that object from one class to another, just like a POJO or DTO pattern.
Why Record ?
As we've seen, the record serves as a data carrier, but the most essential benefit it provides is that it avoids a significant amount of boilerplate code.
When I say "reducing boilerplate code," I mean that we don't have to develop setter and getter methods for parameter values, or declare the hascode(), equal(), or toString() methods to output the values into the console.
When we create a record in Java, we get all of the above methods right away. It is also possible to override the default methods that are provided by the record, such as equal(), hashcode(), or the toString() method.
The record guarantees immutability because all parameter values are final; thus, overriding the values is restricted once they have been declared and initialized. This means that the record is fundamentally immutable.
In Java, the record is also serialized.You can also make a generic record, which will be discussed later in this article.
All the Java record classes are extended by the java.lang.Record, which means we record a class that can't extend another class.
How to create a record in Java?
In Java, we simply use the special keyword i.e; record to create a record. We must additionally declare the data type of the attribute in the parenthesis, as we do in the constructor.
The syntax for the record that we used to generate it is as follows.
package in.learnjavaskills.record.dto;
public record Student(Long id, String firstname, String lastName) {}
Now that you know how to use this record as a data carrier—a DTO or POJO—you must be wondering how to do it. Let's look at how we may use this record in our Java code in the following part.
import in.learnjavaskills.record.dto.Student;
public class Application
{
public static void main(String[] args)
{
Student student = new Student(1L, "John", "Dat");
System.out.println("Student details is : " + student.toString());
}
}
To build an instance of the record class in Java, we simply utilize the constructor of the record given by the JVM. In the preceding example, we built a student record and used the constructor to create an instance, as well as illustrated how to use the toString() method.
You can also retrieve the individual attribute values set in the constructor, as seen below.
package in.learnjavaskills.record;
import in.learnjavaskills.record.dto.Student;
public class Application
{
public static void main(String[] args)
{
Student student = new Student(1L, "John", "Dat");
System.out.println("student id : " + student.id());
System.out.println("student first name : " + student.firstname());
System.out.println("student last name : " + student.lastName());
}
}
Constructor in the record
If you do not create a constructor in the Java record, the JVM will use the default constructor with the same number of attributes, which implies all-argument constructors that you used while creating the record.
In a Java record, how to override the default constructor?
Before we override the default constructor, let us first understand why you need to.
There may be requirements; for example, you may need to validate the value before assigning it to the record attribute (parameter), or you may want to assign the default values. In this case, you may fulfill your request by overriding the default all-argument constructor.
The Java record has three types of constructors, which are as follows:
- Canonical Constructor
- Compact Constructor
- Non-Canonical Constructor
Canonical Constructor
Canonical constructors are ones that rely on the same number of attributes (parameters) in the constructor as when the attribute is defined in the record.
To initialize the variable for the Java record, you must use this keyword in the canonical constructor.
package in.learnjavaskills.record.dto;
import java.util.Objects;
public record Student(Long id, String firstname, String lastName)
{
public Student(Long id, String firstname, String lastName)
{
Objects.requireNonNull(firstname, "first name is mandatory");
Objects.requireNonNull(lastName, "last name is mandatory");
if (firstname.length() > 50)
throw new IllegalArgumentException("firstname length must be less then or equal to 50 characters");
if (lastName.length() > 50)
throw new IllegalArgumentException("lastname length must be less then or equal to 50 characters");
this.id = id;
this.firstname = firstname;
this.lastName = lastName;
}
}
Compact constructor
Records enable us to significantly eliminate boilerplate code, but in the example above, we have created a constructor with the same number of parameters as were present when the record was initially created.
In what precise ways does it minimize boilerplate code? You must be wondering, then.
We can also create the compact constructor instead of the canonical constructor thanks to the record.That is precisely what we shall discover later.
package in.learnjavaskills.record.dto;
import java.util.Objects;
public record Student(Long id, String firstname, String lastName)
{
public Student
{
Objects.requireNonNull(firstname, "first name is mandatory");
Objects.requireNonNull(lastName, "last name is mandatory");
if (firstname.length() > 50)
throw new IllegalArgumentException("firstname length must be less then or equal to 50 characters");
if (lastName.length() > 50)
throw new IllegalArgumentException("lastname length must be less then or equal to 50 characters");
}
}
To make a compact constructor, simply delete all argument parameters, including the parenthesis, as well as the variable's initialization from the canonical constructor.
Non-Canonical Constructor
We can write a custom constructor in the record, much like the canonical constructor, and this custom constructor is known as the non-canonical constructor.
But why do you need the canonical constructor to start off with?
There are caveats; you may not receive all of the properties (parameters) required to generate the record, or you may receive a different data type. In such a circumstance, you can create a non-canonical constructor and handle it within that constructor.
In the next example, I'll show you how to use the non-canonical constructor in your Java record.
package in.learnjavaskills.record.dto;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
public record Student(Long id, String firstname, String lastName)
{
public static final String DEFAULT_LAST_NAME = "-";
public Student
{
Objects.requireNonNull(firstname, "first name is mandatory");
if (firstname.length() > 50)
throw new IllegalArgumentException("firstname length must be less then or equal to 50 characters");
}
public static Long generateId()
{
return LocalDateTime.now().toEpochSecond(ZoneOffset.MAX);
}
}
As seen in the above code sample, if you create a non-canonical constructor, you must call the canonical constructor using this keyword and The first statement in the non-canonical constructor should be this.(alert-passed)
Define your own method in the Java records.
As you may know, the Java record is a special class in Java; hence, you can define your own method in the Java record. In the next example, I'll show you how to build and utilize your own method in the Java record.
package in.learnjavaskills.record.dto;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
public record Student(Long id, String firstname, String lastName)
{
public Student
{
Objects.requireNonNull(firstname, "first name is mandatory");
if (firstname.length() > 50)
throw new IllegalArgumentException("firstname length must be less then or equal to 50 characters");
}
public static Long generateId()
{
return LocalDateTime.now().toEpochSecond(ZoneOffset.MAX);
}
}
package in.learnjavaskills.record;
import in.learnjavaskills.record.dto.Student;
public class Application
{
public static void main(String[] args)
{
Student student = new Student(Student.generateId(), "Rahul", "kapor");
System.out.println("student id : " + student.id());
System.out.println("student first name : " + student.firstname());
System.out.println("student: " + student.toString());
}
}
That is how you may construct your own method in the java record. We may also override the default equal, hashcode, and toString methods.
Static variables in the Java record.
Static variables are also supported in the Java record. Next, let's look at how we may use the static variable in the Java record.
package in.learnjavaskills.record.dto;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
public record Student(Long id, String firstname, String lastName)
{
public static final String DEFAULT_LAST_NAME = "-";
public Student
{
Objects.requireNonNull(firstname, "first name is mandatory");
if (firstname.length() > 50)
throw new IllegalArgumentException("firstname length must be less then or equal to 50 characters");
}
public static Long generateId()
{
return LocalDateTime.now().toEpochSecond(ZoneOffset.MAX);
}
}
package in.learnjavaskills.record;
import in.learnjavaskills.record.dto.Student;
public class Application
{
public static void main(String[] args)
{
Student student = new Student(Student.generateId(), "Rahul", Student.DEFAULT_LAST_NAME);
System.out.println("student default last name : " + student.lastName());
}
}
Generic in the Java record
Java's generics feature enables programmers to write reusable code that can operate on many data types.This is accomplished through the use of type parameters, which serve as placeholders for real data types.
Generics have various advantages, including:
- Type safety: Generics help in the prevention of type mistakes during compilation. For example, attempting to add a Long object to a list of Long objects would result in an error from the compiler.
- Code reuse: Generic code may be utilized with different data types without requiring rewriting. This makes the code more manageable and extendable.
Let's look at how we can use generics in the Java record.
package in.learnjavaskills.record.dto;
public record CollectionsOfData<T>(T value)
{}
package in.learnjavaskills.record;
import in.learnjavaskills.record.dto.CollectionsOfData;
import java.util.List;
public class Application
{
public static void main(String[] args)
{
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
CollectionsOfData<List<Integer>> collectionsOfData = new CollectionsOfData<>(numbers);
System.out.println("collections of data : " + collectionsOfData.value());
}
}
The output of the above code is as follows:
collections of data : [1, 2, 3, 4, 5]
Advantages of Using Java Records
Records may be used to build a wide range of data structures, including DTOs, POJOs, and value objects. The following are some of the advantages of utilizing Java records:
- Increased readability and maintainability of the code: Records are more compact and simpler to understand than standard Java classes since the constructor, getters(), setters(), equals(), hashCode(), and toString() methods are not explicitly defined. This improves the readability and maintainability of your code.
- Reduce significant amounts of error: Numerous methods are automatically generated by records, which might help your code be more error-free. For instance, the compiler will make sure that the hashCode() and equals() methos's are implemented properly.
- Immutability When it comes to immutability, records are safer and easier to pick since they are immutable by nature. This can help with error prevention and code quality enhancement.
Conclusion
In this course, we learnt about the Java record and explored its benefits, including how the Java record gets rid of a significant quantity of boilerplate code.
We gained knowledge about constructors in the Java record, which helped us grasp the canonica and non-canonica constructors. We also learn how to develop the compact constructor to remove boilerplate code from the canonical constructor.
In the Java record, we implemented the custom method and the static variable.
The most essential thing we learn is how to use generics in Java to help us develop reusable code.
Keep learning and keep growing.