8 Unnamed Variables and Patterns
Unnamed variables are variables that can be initialized but not used. 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. You denote both with the underscore character
(_
).
For background information about unnamed variables and patterns, see JEP 456.
Unnamed Variables
You can use the underscore keyword (_
) 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’s 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 omit or 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 8-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 |
|
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 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");
}