BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News JEP 443: Unnamed Patterns and Variables Aims to Improve Java Code Readability

JEP 443: Unnamed Patterns and Variables Aims to Improve Java Code Readability

JEP 443, Unnamed Patterns and Variables (Preview), has been Completed from Targeted status for JDK 21. This preview JEP proposes to "enhance the language with unnamed patterns, which match a record component without stating the component's name or type, and unnamed variables, which can be initialized but not used." Both of these are denoted by the underscore character, as in r instanceof _(int x, int y) and r instanceof _. This is a preview language feature.

Unnamed patterns are designed to streamline data processing, particularly when working with record classes. They allow developers to elide the type and name of a record component in pattern matching, which can significantly improve code readability. For example, consider the following code snippet:

if (r instanceof ColoredPoint(Point p, Color c)) {
    // ...
}

In this instance, if the Color c component is not needed in the if block, it can be laborious and unclear to include it in the pattern. With JEP 443, developers can simply omit unnecessary components, resulting in cleaner, more readable code:

if (r instanceof ColoredPoint(Point p, _)) {
    // ...
}

Unnamed variables are useful in scenarios where a variable must be declared, but its value is not used. This is common in loops, try-with-resources statements, catch blocks, and lambda expressions. For instance, consider the following loop:

for (Order order : orders) {
    if (total < limit) total++;
}

In this case, the order variable is not used within the loop. With JEP 443, developers can replace the unused variable with an underscore, making the code more concise and clear:

for (_ : orders) {
    if (total < limit) total++;
}

Unnamed patterns and variables are a preview feature, disabled by default. To use it, developers must enable the preview feature to compile this code, as shown in the following command:

javac --release 21 --enable-preview Main.java

The same flag is also required to run the program:

java --enable-preview Main

However, one can directly run this using the source code launcher. In that case, the command line would be:

java --source 21 --enable-preview Main.java

The jshell option is also available but requires enabling the preview feature as well:

jshell --enable-preview

Let’s look at a few more advanced use cases of unnamed patterns and variables introduced in JEP 443:

Unnamed patterns can be particularly useful in nested pattern-matching scenarios where only some components of a record class are required. For example, consider a record class ColoredPoint that contains a Point and a Color. If you only need the x coordinate of the Point, you can use an unnamed pattern to omit the y and Color components:

if (r instanceof ColoredPoint(Point(int x, _), _)) {
    // ...
}

Unnamed pattern variables can be beneficial in switch statements where the same action is executed for multiple cases, and the variables are not used. For example:

switch (b) {
    case Box(RedBall _), Box(BlueBall _) -> processBox(b);
    case Box(GreenBall _) -> stopProcessing();
    case Box(_) -> pickAnotherBox();
}

In this example, the first two cases use unnamed pattern variables because their right-hand sides do not use the box's component. The third case uses the unnamed pattern to match a box with a null component.

Unnamed variables can be used in lambda expressions where the parameter is irrelevant. For example, in the following code, the lambda parameter v is not used, so its name is irrelevant:

stream.collect(Collectors.toMap(String::toUpperCase, _ -> "No Data"));

In try-with-resources statements, a resource represents the context in which the code of the try block executes. If the code does not use the context directly, the name of the resource variable is irrelevant. For example:

try (var _ = ScopedContext.acquire()) {
    // No use of acquired resource
}

Unnamed variables can be used in catch blocks where the name of the exception parameter is irrelevant. For example:

try {
    int i = Integer.parseInt(s);
} catch (NumberFormatException _) {
    System.out.println("Bad number: " + s);
}

Notably, the underscore character was previously valid as an identifier in Java 10. However, since Java 8, the use of underscore as an identifier has been discouraged, and it was turned into a compile-time error in Java 9. Therefore, it is assumed that a minimal amount of existing and actively maintained code uses underscore as a variable name. In instances where such code does exist, it will need to be modified to avoid using an underscore as a variable name.

Given this, JEP 443 is a significant step towards making Java code more readable and maintainable. This is particularly beneficial in complex data structures where the shape of the structure is just as important as the individual data items within it. By allowing developers to omit unnecessary components and variables, it reduces code clutter and makes the code easier to understand. As developers gain more experience with this feature, it is expected to become an integral part of Java programming.

About the Author

Rate this Article

Adoption
Style

BT