Looking at Java 22: Unnamed Variables & Patterns
In this article, I want to talk about a non-preview feature belonging to Project Amber, JEP 456.
With Java 9, the _
(underscore) became a reserved keyword and no longer a valid identifier.
Now, with the release of Java 22, the new keyword finally gets a specific meaning: something not having a name.
Table of Contents
Everything Needs a Name
There are many scenarios where we declare a variable or are forced to do so without actually needing it. Be it variables in for-each-loops, in try-with-resources blocks, or lambda parameters, we always need to declare names, even if we might only be interested in a side-effect:
With underscore being a keyword since Java 9, I often abused $
(dollar) as a short substitute for unused variable names, especially for lambdas or catch
blocks:
While it’s technically legal to use the dollar sign, convention dictates it shouldn’t be used at all. The Java Language Specification (JLS §3.8) even says:
The dollar sign should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems.
Thankfully, JEP 456 brings in the officially sanctioned alternative!
Names No Longer Required
By making underscore a keyword, it can now be used as a substitute for any occurrences of an unnamed variable or pattern, even in the same context.
Unnamed Variables
First, let’s look at unnamed variables.
The following kinds of declarations support replacing a variable name with an underscore:
- A local variable in a block (JLS §14.4.2)
try
-with-resources (JLS §14.20.3)for
loop (JLS §14.14.1)- Enhanced
for
loop (JLS §14.14.2) - Exception for a
catch
block (JLS §14.20) - Lambda parameter (JLS §15.27.1)
The previous examples look something like this now:
Unlike in other languages, the underscore replaces only the variable/parameter name. The exception to this rule, though, is lambda parameters, as they do not necessarily need an explicit type in the first place.
Unnamed instanceof Record Patterns
Record patterns are a handy tool to destructure them:
However, redeclaring the whole Record for instanceof
is quite cumbersome, especially for nested Records if we don’t need access to all of their components.
With Java 22, an underscore can replace any component, even with or without its type:
The Record itself is not replaceable, as the compiler still needs to deduct which underlying Record is needed to be destructured:
Unnamed switch case Patterns
Using underscore for conditional patterns follows the same rules as instanceof
.
Let’s use sealed classes for a more straightforward example:
Now, we can create a switch
statement working with the different types:
With unnamed patterns, it’s likely that several case
clauses will do the same thing for different patterns.
That’s why the switch
statements itself got changed recently, too!
JLS §14.11.1 changed as follows:
This simple change coming from Java 21’s introduction of switch
patterns simplifies overlapping unnamed patterns:
Conclusion
Finally, having a keyword for unnamed variables and patterns instead of needing to fake them is a great addition to the language. And in good ol’ Java fashion, the change wasn’t hastily introduced, even though its journey started in Java 8 with the introduction of lambdas.
The need for unnamed parameters became more evident in the new lambda syntax, and the general idea of them was discussed in the “Lambda Leftovers” JEP 302. As a sole underscore was a valid identifier so far, the OpenJDK team made its usage a warning in Java 8, to not break any code.
Starting with Java 9, this warning became an error, freeing up the underscore until its rebirth as a keyword, and finally, using that keyword for unnamed variables and patterns.
Resources
- JEP 456: Unnamed Variables & Patterns
- JEP 456: Unnamed Variables & Patterns (Preview)
- JEP 302: Lambda Leftovers
Looking at Java 22
- Intro
- Statements before super (JEP 447)
- Class-File API (JEP 457)
- Foreign Function & Memory API
- [Stream Gatherers (JEP 461)]({{ ref “posts/2024/2024-04-29-looking-at-java-22-stream-gatherers.md” }})