Replacing Wrapper classes with Java Records in Stream processing
Simple case study with Code snippets
Admit it, there is no turning back once you are deep into Java stream processing.
You cringe at the sight of For loops in your old code. Everything is wonderful with stream processing except one. Wrapper classes.
If your data processing is complex, and if there are multiple data transformation steps involved, you would need to create wrapper classes that take a lot of space.
Even if the wrapper classes are immutable(without setters), you will still need to create an inner class within your service class.
This is one of the places, Record classes shine the best.
We can go through this with a case study, I have taken an age old Employee-Employer use case. Consider that we are implementing a portion of a Personnel Management software. Let us call our software, EmployeeSoft ;)
Assume that EmployeeSoft is a SaaS, with a centralised processing line.
We are going to implement a part in EmployeeSoft, where the system is going to update the salary of the Employees. For a list of Employers at the end of a financial year, we need to update the salary of the corresponding Employees based on the hike percentage.
Our data classes are going to look like this.
Now, at the end of March, our EmployeeSoft has collected the list of Employers who want to update the salary of their employees. Now, our service(which we are going to implement) will get this list of data class with the employerId and the hike percentage.
For every employerId, we need to query the database and get the list of Employee data for the employer. And for each of the employee, we compute the new salary based on the hike percentage we got from the EmployerSalaryUpdate class.
If you think of this in for-loops, there would be two for-loops, the outer for-loop would be the list for EmployerSalaryUpdate and inner for-loop would the list of Employee. A simpler implementation would like below.
Now if we want to make use of Java streams’ convenience methods, which also make this a bit of fancy, we go to java stream processing.
An equivalent stream processing code would look like below. We can use a flatMap or mapToMulti(a bit more fancy) to expand the pipeline 1-n.
In our case, 1 employer has n employees. This is also a place where we need to have wrapper classes often, as the subsequent data transformation steps would need data from both EmployerSalaryUpdate class and Employee class.
As you can see, we are only creating an immutable class with final members and getters, still the code transformation from a simple for-loop to stream processing has introduced the need for30 extra lines of code. No harm done here, still it is not pleasant to have such boiler plate code.
This is where we can use Record classes introduced in Java 16.
What are record classes?
Record classes are replacements for immutable classes. It is not a replacement for all the boilerplate code like lombok does. But we can emulate record classes behaviour with lombok.
For every record class you create, Java would create the required hashcode(), equals(), a canonical constructor as in the record header and getter methods internally.
Example record classes:
As you can see in line number 9, you can also manipulate the values as you do with constructors usually.
An important point to note here is, if your object would change its state in course of its lifetime, meaning if you need setters, then Record classes will be of no use to you.
Coming back to our example, an equivalent code using record classes would look like below
In line number 18, I have created a record class. Since this record class is created within a method, we call this a Local Record class. This will not be available outside this method. These are very useful within data transformation steps.
As you can see here, the number of lines has significantly decreased.
Although record classes are immutable, if the member classes are mutable, then you could still change the state of the record, but this is not advisable. When you are going to create immutable classes, always use immutable members. For example, you could still do the below. But it is not advised to do so.