Unnamed Variables and Patterns
An unnamed variable is a variable declared with the name _ (a single underscore). It tells the reader and the compiler: "a variable is required here, but I'm not going to use it." An unnamed pattern is a _ inside a record pattern, meaning "there's a component here, and I don't care what it is." Both became standard in Java 22 (JEP 456). In this lesson, you will learn every place _ can be used, and how it lets one switch case handle several types.
Why It Exists
Java often forces you to name things you don't use: the loop variable when you only count, the exception in a catch you only log, the key in a lambda that only needs the value. Unused names are noise, and tools warn about them. _ makes the intent clear and can be used many times in the same scope, because it never really declares a name.
Where You Can Use _
| Place | Example |
|---|---|
| Local variable | int _ = queue.remove(); |
Enhanced for loop variable | for (String _ : names) count++; |
catch parameter | catch (NumberFormatException _) { ... } |
| Lambda parameter | map.forEach((_, v) -> ...) |
| try-with-resources | try (var _ = new Timer()) { ... } |
| Type pattern in a case | case Circle _ -> ... |
| Component in a record pattern | Point(int x, _) |
It can't be used for fields, method parameters, or method names, and you can never read an unnamed variable.
Example: Everyday Uses
Let's see an example that uses _ in loops, catch, lambdas and try-with-resources:
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class Unused {
static class Timer implements AutoCloseable {
Timer() {
System.out.println("timer started");
}
public void close() {
System.out.println("timer stopped");
}
}
public static void main(String[] args) {
List<String> names = List.of("Navika", "Srihitha", "Advitha");
int count = 0;
for (String _ : names) {
count++;
}
System.out.println("count = " + count);
try {
Integer.parseInt("abc");
} catch (NumberFormatException _) {
System.out.println("not a number");
}
Map<String, Integer> marks =
new TreeMap<>(Map.of("Navika", 91, "Advitha", 78));
marks.replaceAll((_, v) -> v + 5);
System.out.println(marks);
try (var _ = new Timer()) {
System.out.println("working");
}
}
}count = 3
not a number
{Advitha=83, Navika=96}
timer started
working
timer stoppedExplanation:
- The loop only counts, so the element is unnamed.
- The
catchonly needs to know the exception happened. replaceAll((_, v) -> v + 5)adds 5 to every value; the key isn't needed.- try-with-resources still calls
close()on the unnamedTimer. That's useful for resources that only matter for their opening and closing, such as a lock holder or a timer.
Unnamed Patterns in Switch
In a switch, _ lets one case handle several types when it doesn't need the value. Before Java 22 this wasn't possible, because each type pattern needed its own variable:
public class Corners {
sealed interface Shape permits Circle, Square, Triangle { }
record Circle(double r) implements Shape { }
record Square(double side) implements Shape { }
record Triangle(double b, double h) implements Shape { }
record Point(int x, int y) { }
static String corners(Shape s) {
return switch (s) {
case Circle _ -> "no corners";
case Square _, Triangle _ -> "has corners";
};
}
static String kind(Object o) {
return switch (o) {
case Integer _, Long _ -> "whole number";
case Double _, Float _ -> "decimal";
default -> "other";
};
}
public static void main(String[] args) {
System.out.println(corners(new Triangle(2, 3)));
System.out.println(kind(5L));
System.out.println(kind(2.5f));
Object o = new Point(3, 4);
if (o instanceof Point(int x, _)) {
System.out.println("x = " + x);
}
}
}has corners
whole number
decimal
x = 3Explanation: case Square _, Triangle _ matches either type. It's still exhaustive: together with Circle _, every Shape is covered. Point(int x, _) takes only the first component; _ stands for the whole pattern of the second.
The Rules
A case with several patterns can't bind a named variable, because the variable wouldn't be set when the other pattern matched:
U5.java:4: error: illegal fall-through from a pattern
case Integer _, String s -> "x";
^
(the current case label is missing a break)
1 errorAnd an unnamed variable can never be read:
U2.java:4: error: underscore not allowed here
System.out.println(_);
^
1 errorIn very old code (Java 8 and earlier), _ was a legal variable name. Since Java 9 it has been a keyword, which is why it was free to take this new meaning.
Common Mistakes
Trying to read _. It isn't a variable you can use; give it a real name if you need it.
Naming one variable in a multi-pattern case. All patterns in a case with several patterns must be unnamed.
Using _ for method parameters. That isn't allowed; only local variables, lambda parameters, catch parameters, loop variables, resources and patterns can be unnamed.
Hiding real errors. catch (Exception _) { } makes failures invisible.
Interview Corner
What are unnamed variables and patterns?
Introduced as standard in Java 22, the underscore _ declares a variable or pattern that is required by the syntax but not used. It can be used for local variables, enhanced-for variables, catch parameters, lambda parameters, try-with-resources resources, type patterns (case Circle _) and record pattern components (Point(int x, _)). It can appear several times in the same scope and can never be read. In switch, it allows one case to list several patterns, such as case Square _, Triangle _ ->, as long as none of them binds a named variable.
Practice
- Easy: Use
_in an enhancedforloop that prints "Hello" once for each name in a list. Then explain why the counteriinfor (int i = 0; i < 5; i++)can't be named_. - Harder: Write a switch over a sealed
Eventinterface withClick,KeyPressandScrollrecords, whereClickandKeyPressshare one case using unnamed patterns.
Key Takeaways
Was this tutorial helpful?