Record Patterns in Java 21 - Sip of Java

Record patterns were added in Java 21, providing an easier way of extracting data from records. Let’s take a look!

Records Reviewed

Records were added back in Java 16 with JEP 395. Records could be declared in a single line, like in this example:

record Person(String fName, String lName, int age){}

While developers will notice the reduction in boilerplate code, the true intent of records was a transparent way of defining data as data. This is accomplished by placing several constraints on records, including:

  • All fields are final
  • Record classes are implicitly final
  • Declaration of instance fields is prohibited

In addition, records also provided a few features:

  • All record classes include canonical constructors
  • All record classes include accessors to components

These constraints and features made records an ideal candidate for adding deconstructors.

Deconstructors

Deconstructors are, as the name implies, the inverse of constructors. Where a constructor is about creating and adding data to an object, a deconstructor is about extracting data from it, like in this example:

record Name(String fName, String lName, int age) {};
Name host = new Name("William","Korando", 37);

if(host instanceof Name(String fName, String lName, int age)) {
   String printName = lName + ", " + fName + " age: " + age;
}

Component Matching

Record patterns are flexible. A pattern variable’s type only needs to be compatible with the record’s components. In the below example var is used instead:

record Name(String fName, String lName, int age) {};
Name host = new Name("William","Korando", 37);

if(host instanceof Name(var first, var last, var a)) {
   String printName = last + ", " + first + " " + a;
}

Note here that the pattern variable names also differ from the name of the record components. This helps make record patterns more flexible and avoids potential variable name conflicts.

Nested patterns

Record patterns can also be nested, allowing for easy deconstruction of complex records:

record Name(String fName, String lName, int age) {};
record Address(...) {};
record Person(Name name, Address address) {};
Name host = new Name("William","Michael", 37);
Address address = new Address(...);
Person person = new Person(host, address);

if(person instanceof Person(
		Name(var first, var last, var middle), Address(...))) {
	String printName = last + ", " + first + " age: " + age;
}

Record Patterns in switch

You can use record patterns in switch, which now supports pattern matching with Java 21.

record Name(String fName, String lName, String mName) {};
Name host = new Name("William", "Michael", "Korando");

String printName = switch(person){
	case Name(var fName, var lName, var mName) -> lName + ", " + fName + " " + mName;
};

Data-Oriented Programming

Records and record patterns are an essential element of Java’s emerging data-oriented programming story. This is a story that will continue to be added to, like, for example, JEP 443, which introduced unnamed named patterns and variables, as a preview feature in Java 21. Be sure to check the additional reading section for Brian Goetz’s article Data-Oriented Programming in Java to get the vision of how it will be implemented.

Additional Reading

JEP 440 - Record Patterns

JEP 395 - Records

Data-Oriented Programming

JEP 443 - Unnamed Patterns and Variables

Happy coding!