Unnamed Patterns and Variables
Unnamed patterns can appear in a pattern list of a record pattern,
and always match the corresponding record component. You can use them instead of a type
pattern. They remove the burden of having to write a type and name of a pattern variable
that's not needed in subsequent code. Unnamed variables are variables that can be
initialized but not used. You denote both with the underscore character
(_
).
Note:
This is a preview feature. A preview feature is a feature whose design, specification, and implementation are complete, but is not permanent. A preview feature may exist in a different form or not at all in future Java SE releases. To compile and run code that contains preview features, you must specify additional command-line options. See Preview Language and VM Features.For background information about unnamed patterns and variables, see JEP 443.
Unnamed Patterns
Consider the following example that calculates the distance between two
instances of ColoredPoint
:
record Point(double x, double y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
double getDistance(Object obj1, Object obj2) {
if (obj1 instanceof ColoredPoint(Point p1, Color c1) &&
obj2 instanceof ColoredPoint(Point p2, Color c2)) {
return java.lang.Math.sqrt(
java.lang.Math.pow(p2.x - p1.x, 2) +
java.lang.Math.pow(p2.y - p1.y, 2));
} else {
return -1;
}
}
The example doesn't use the Color
component of the
ColoredPoint
record. To simplify the code and improve
readability, you can omit or elide the type patterns Color
c1
and Color c2
with the unnamed pattern
(_
):
double getDistance(Object obj1, Object obj2) {
if (obj1 instanceof ColoredPoint(Point p1, _) &&
obj2 instanceof ColoredPoint(Point p2, _)) {
return java.lang.Math.sqrt(
java.lang.Math.pow(p2.x - p1.x, 2) +
java.lang.Math.pow(p2.y - p1.y, 2));
} else {
return -1;
}
}
Alternatively, you can keep the type pattern's type and elide just its name:
if (obj1 instanceof ColoredPoint(Point p1, Color _) &&
obj2 instanceof ColoredPoint(Point p2, Color _))
No value is bound to the unnamed pattern variable. Consequently, the highlighted statement in the following example is invalid:
if (obj1 instanceof ColoredPoint(Point p1, Color _) &&
obj2 instanceof ColoredPoint(Point p2, Color _)) {
// Compiler error: the underscore keyword '_" is only allowed to
// declare unnamed patterns, local variables, exception parameters or
// lambda parameters
System.out.println("Color: " + _);
// ...
}
Also, you can't use an unnamed pattern as a top-level pattern:
// Compiler error: the underscore keyword '_' is only allowed to
// declare unnamed patterns, local variables, exception parameters or
// lambda parameters
if (obj1 instanceof _) {
// ...
}
You can use unnamed patterns in switch
expressions and
statements:
sealed interface Employee permits Salaried, Freelancer, Intern { }
record Salaried(String name, long salary) implements Employee { }
record Freelancer(String name) implements Employee { }
record Intern(String name) implements Employee { }
void printSalary(Employee b) {
switch (b) {
case Salaried r -> System.out.println("Salary: " + r.salary());
case Freelancer _ -> System.out.println("Other");
case Intern _ -> System.out.println("Other");
}
}
You may use multiple patterns in a case
label provided
that they don't declare any pattern variables. For example, you can rewrite the
previous switch
statement as follows:
switch (b) {
case Salaried r -> System.out.println("Salary: " + r.salary());
case Freelancer _, Intern _ -> System.out.println("Other");
}
Unnamed Variables
You can use the underscore keyword (_
) not just as a pattern in a
pattern list, but also as the name of a local variable, exception, or lambda
parameter in a declaration when the value of the declaration isn't needed. This is
called an unnamed variable, which represents a variable that'ss being
declared but it has no usable name.
Unnamed variables are useful when the side effect of a statement is more important than its result.
Consider the following example that iterates through the elements of the
array orderIDs
with a for
loop. The side effect of
this for
loop is that it calculates the number of elements in
orderIDs
without ever using the loop variable
id
:
int[] orderIDs = {34, 45, 23, 27, 15};
int total = 0;
for (int id : orderIDs) {
total++;
}
System.out.println("Total: " + total);
You can use an unnamed variable to elide the unused variable id
:
int[] orderIDs = {34, 45, 23, 27, 15};
int total = 0;
for (int _ : orderIDs) {
total++;
}
System.out.println("Total: " + total);
The following table describes where you can declare an unnamed variable:
Table 6-1 Valid Unnamed Variable Declarations
Declaration Type | Example with Unnamed Variable |
---|---|
A local variable declaration statement in a block |
Note that you don't have to assign the value returned by Queue::remove to a variable, named or unnamed. You might want to do so to signify that a lesser known API returns a value that your application doesn't use. |
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 |
|