Suppose that you want to limit the values that can be passed to a field in your data class during its instantiation. This is feasible; just override the default constructor. The following is an example:
record Emp(String name, int age) { // override default constructor @Override public Emp(String name, int age) { // validate age if (age > 70) throw new IllegalArgumentException("Not employable above 70
years"); else { // call default constructor default.this(name, age); } } }
Similarly, you can override the default implementations of object methods, such as equals(), hashCode(), and toString(), and other methods, such as the accessor methods.
Overriding the default behaviors of the methods of your data class doesn't defeat the purpose of their creation. They are still working as data classes, with finer control of their functionality. Let's compare this with POJOs, which were used to model data classes previously. The compiler doesn't auto-generate any methods for a POJO. So, a user still needs to read all of the code, looking for code that isn't the default implementation of its methods. In the case of data classes, this overridden behavior is very explicit. So, a user doesn't have to worry about reading all of the code; they can assume default implementation of the behavior, which hasn't been overridden by the developer.