In my previous post, I introduced you to the new construct called Records which was introduced in Java 16.
In this post, I will go a bit deeper into how you can override the default constructor of a record
which is different from the no-arg default constructor of a class
. Default constructor in a record
is the one that takes all the record parameters as the arguments to the constructor method and it is called a canonical constructor.
Let me create a SimpleProfileRecord
which will override the canonical constructor:
record SimpleProfileRecord(String firstName, String lastName){
public SimpleProfileRecord(String firstName, String lastName){
System.out.println("This is called");
this.firstName = firstName;
this.lastName = lastName;
}
}
So when I create an instance of this record and print its contents I would get:
Clik here to view.

System.out.println(new SimpleProfileRecord("Mohamed", "Sanaulla"));
If I try to provide other non-canonical constructors, I would get an error as shown in the image below which means any constructor defined in the record should end up initializing all the properties. This makes total sense because all the fields of a record are declared as final
which mandates that these should be initialized before the object is eligible for use.
Clik here to view.

Instead of redeclaring the complete constructor definition Java provides a less verbose way to override the canonical constructor which I have shown in the code below:
record UppercaseProfileRecord(String firstName, String lastName){
UppercaseProfileRecord {
firstName = firstName.toUpperCase();
lastName = lastName.toUpperCase();
}
}
The compiler implicitly derives the arguments to the constructor block from the record definition. Sometimes I can add custom code to initialize certain properties based on some conditions like shown below:
record AnotherProfileRecord(String firstName, String lastName, String name){
AnotherProfileRecord{
if(isEmpty(firstName)){
throw new IllegalArgumentException("First name cannot be empty");
}
if(isEmpty(lastName)){
throw new IllegalArgumentException("Last name cannot be empty");
}
if(Objects.isNull(name) || name.trim().length() == 0){
name = firstName.concat(" ").concat(lastName);
}
}
boolean isEmpty(String str){
return Objects.isNull(str) || str.trim().length() == 0;
}
}
In the above code snippet name
is initialized from firstName
and lastName
only if the value passed to it was empty. Also, look at how I have provided a custom method isEmpty
in the record.
Records cannot extend other classes or records but they can implement interfaces as shown in the code below:
record FileServiceResponse(InputStream inputStream, Long fileSize, URL presignedUrl)
implements AutoCloseable{
@Override
public void close() throws Exception {
if(inputStream != null){
inputStream.close();
}
}
}