Java 21 Unnamed Patterns and Variables (with Examples)

In Java 21, the unnamed patterns and variables feature (JEP-443) aims to strike a balance between clarity and brevity by using an underscore character (_), to make code more concise and easier to read.

This tutorial discusses the motivation behind this feature, discusses its usescases, and then demonstrates these through various examples.

1. Motivation: The Brevity in Code

Let’s first understand a scenario where brevity in the code is avoidable. We are using the record type Employee which has few basic attributes, including another record type Department.

record Department(Long id, String name, Boolean status) {
  // Constructor and methods (if any)
}

record Employee(Long id, String name, LocalDate dateOfBirth, Boolean status, Department department) {
  // Constructor and methods (if any)
}

In Java, records are treated as immutable data carriers and are intended to be used in places where a class is created only to act as a plain data carrier. Using its simple syntax, they eliminate all the boilerplate code needed to set() and get() the data from the instance.

Also, the record patterns feature disaggregates an instance of a record into its components. For example, if we want to write a code that checks for the type of Employee and then uses its components, we can write as follows:

Department department = new Department(1L, "HR", true);

if (department instanceof Department(Long id, String name, Boolean status)) {
  
  System.out.println("Department Name is : " + name);  // Prints 'Department Name is : HR'
}

However, when dealing with record patterns, it’s common to need only some components and not all of them. In our previous example, we are only using the component 'name'. The other components id and status, just increase the brevity without increasing the readability.

Similarly, when patterns are deeply nested, unnamed patterns improve code readability, especially when extracting data. Consider one more example having nested record patterns:

Employee employee = new Employee(101L, "John Doe", LocalDate.of(1990, 5, 15), true, department);
Department department = new Department(1L, "HR", true);

if (employee instanceof Employee(Long id, String name, LocalDate dateOfBirth, Boolean status, 
    Department(Long depId, String depName, Boolean depStatus))) {

  System.out.printf("Employee %d works in department %s.", id, depName);
}

//Prints 'Employee 101 works in department HR.'

We can see that even though we are not using most of the components, we are forced to declare them while destructuring the records; otherwise, the program will not compile.

The new feature is intended to remove the brevity in the code.

2. Introducing Unnamed Patterns and Variables

In Java 21, unnamed patterns and variables represent the record components and local variables as an underscore (_) to denote that we’re not interested in them.

2.1. Unnamed Patterns

Unnamed patterns are used during destructuring of a record into its components.

Using the new feature, we can rewrite the previous example in a more concise manner as follows. Notice we replaced the id and name components with underscore (_).

if (department instanceof Department(_, String name, _)) {
  
  System.out.println("Department Name is : " + name);  // Prints 'Department Name is : HR'
}

It looks even more clean when we use this feature with nested record patterns.

if (employee instanceof Employee(Long id, _, _, _, Department(_, String depName, _))) {

  System.out.printf("Employee %d works in department %s.", id, depName);
}

In the above code example, we are clearly communicating that we are interested in only certain components, and other components are only for the sake of syntax.

In the same way, we can use the underscore character with the nested record also, if they are not used. Consider the following example, we want to use only the employee id for some database operations and other components are not required at all.

if (employee instanceof Employee(Long id, _, _, _, _)) {

  //Use employee id
  System.out.println("Employee Id is : " + id);  // Prints 'Employee Id is : 101'
}

2.2. Unnamed Variables

An unnamed variable is represented with underscore (_) character and denotes that the variable is not used in its scope. This helps in often-seen situations where we declare variables to satisfy the compiler but don’t intend to use it.

While unnamed patterns are related to the destructuring of record components, unnamed variables introduce the concept to more places.

Unnamed variable cannot be written or read after it has been initialized.

As of Java 21, we can use unnamed variables in the following places:

  • A local variable declaration statement in a block.
  • A resource specification of a 'try-with-resources' statement.
  • The header of a basic 'for' statement.
  • The header of an 'enhanced for loop'.
  • An exception parameter of a 'catch' block.
  • A formal parameter of a lambda expression.

Let us see a few examples to understand in more detail.

This example demonstrates creating a local unnamed variable.

int _ = q.remove(); // We don't need the result

The following example demonstrates using an unnamed variable in catch block.

String input = ...;

try { 
    int i = Integer.parseInt(input);
    // use i
} catch (NumberFormatException _) { 
    System.out.println("Invalid input: " + input);
}

Using an unnamed variable in a simple for-loop. Notice we are invoking the runOnce() function when the loop starts, but we are not using its result. So, we demote the result using underscore.

for (int i = 0, _ = runOnce(); i < arr.length; i++) {

  // ... code that uses 'i' ...
}

In all the above examples where variables are unused, and their names are irrelevant, we have simply declared those variables with no name.

3. Benefits of Unnamed Patterns and Variables

Now we have a good idea of what unnamed patterns and variables are, let us recap their advantages.

The most glaring benefit is that unnamed patterns and variables help in eliminating unnecessary information from the programmer’s eyes. This can make to code easier to read and understand for us.

This enhanced readability reduces the confusion during code maintenance. Unnamed variables clearly convey that a value isn’t intended for use and other programmers cannot modify it accidently.

4. Conclusion

Introduced in JEP-443, unnamed patterns and variables are denoted by the underscore character (_), as a means to simplify record pattern destructuring and write concise code in other parts of the applications.

When we embrace these new features, we may greatly benefit when dealing with patterns, variables, and the overall maintainability of a Java application code.

Happy Learning !!

Source Code on Github

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.