Unnamed variables and patterns let us write the character _ in place of a variable or a pattern that the code never uses, so the compiler and the reader both know the value is ignored. The feature is final since Java 22 (JEP 456) after a preview in Java 21 (JEP 443).
We use _ for exception parameters we do not read, for lambda parameters that an API forces on us, for record components we do not need in a pattern, and for local variables that exist only for a side effect. The code compiles on Java 22 and later without any flag.
The following example uses _ in a catch block, a lambda and a record pattern.
record Track(String title, String artist, int seconds) {}
static int parseSeconds(String text) {
try {
return Integer.parseInt(text.strip());
} catch (NumberFormatException _) {
return 0;
}
}
int valid = parseSeconds(" 215 "); // 215
int invalid = parseSeconds("3:35"); // 0
Map<String, List<String>> byArtist = new HashMap<>();
byArtist.computeIfAbsent("Adele", _ -> new ArrayList<>()).add("Hello");
List<String> songs = byArtist.get("Adele"); // [Hello]
Object item = new Track("Hello", "Adele", 295);
int length = item instanceof Track(_, _, int secs) ? secs : 0; // 295
Notice that _ can appear several times in one pattern, because it declares nothing. We cover the three forms of _, every place where an unnamed variable is allowed, the places where it is a compile error, and the cases where an unused variable points to a design problem rather than a job for _.
1. Unnamed Variables vs Unnamed Patterns
JEP 456 uses _ in three related ways. All three mean “there is a value here, and we do not need it”, and none of them creates a name that later code can read.
| Form | Example | Where |
|---|---|---|
| Unnamed variable | catch (IOException _) | Local variables, exception parameters, lambda parameters, loop variables, try-with-resources |
| Unnamed pattern variable | case Song _ | Type patterns at the top level or nested in a record pattern |
| Unnamed pattern | Track(_, _, int secs) | Only nested inside a record pattern, matches any value including null |
The _ character has a history. Java 8 accepted _ as a variable name with a warning, Java 9 made it a compile error (JEP 213) to reserve it, and Java 21 gave it the new meaning as a preview. Names that only start with _, such as _total, and the _ separators in number literals such as 1_000 are not affected.
| Java version | Meaning of a single _ |
|---|---|
| Java 8 and earlier | Legal identifier, warning in Java 8 |
| Java 9 to 20 | Compile error, reserved keyword |
| Java 21 | Unnamed variables and patterns, preview (–enable-preview) |
| Java 22 and later | Unnamed variables and patterns, final |
2. Unnamed Variables
An unnamed variable is a declaration with _ as its name. It can be initialized, but it can never be read or assigned afterwards, and because it has no name, several unnamed variables can sit in the same scope without a conflict. JEP 456 allows unnamed variables in six kinds of declarations, all of which are local to a method or a lambda.

2.1. Exception Parameters
A catch block often reacts to the exception type and ignores the exception object, as in parseSeconds() in the intro. With catch (NumberFormatException _), a reviewer can see that the block does not log or rethrow the exception, and static analysis tools do not report an unused variable.
2.2. Lambda Parameters
Many functional interfaces pass more arguments than a lambda needs. The Map methods computeIfAbsent(), replaceAll() and forEach() are typical examples, because the key or the value is often irrelevant.
Map<String, Integer> plays = new LinkedHashMap<>();
plays.put("Hello", 3);
plays.put("Skyfall", 5);
plays.replaceAll((_, count) -> count + 1);
String updated = plays.toString(); // "{Hello=4, Skyfall=6}"
BiFunction<String, Integer, String> label = (_, count) -> count + " plays";
String text = label.apply("Hello", 4); // "4 plays"
A lambda can use _ for several parameters, such as (_, _) -> 0. Each _ is a separate unnamed parameter.
2.3. Local Variables
Sometimes we call a method only for its side effect, but we still want to show that it returns a value. An unnamed local variable documents that the result is ignored on purpose.
Queue<String> queue = new ArrayDeque<>(List.of("intro", "ad", "song"));
String _ = queue.poll();
String _ = queue.poll();
String nextUp = queue.peek(); // "song"
Both String _ declarations compile in the same block, because neither one introduces a name. A local unnamed variable needs an initializer, and var _ = queue.poll(); works too.
2.4. Loop Variables
The header of an enhanced for loop and the initialization part of a basic for loop can declare unnamed variables. The enhanced for case fits an Iterable that has no size() method, when we only count the elements.
static int countTracks(Iterable<Track> playlist) {
int total = 0;
for (Track _ : playlist) {
total++;
}
return total;
}
int trackCount = countTracks(List.of(new Track("A", "X", 120), new Track("B", "Y", 90))); // 2
2.5. Try-with-Resources
Some resources do their work in their constructor and their close() method, such as a tracing span, a lock holder or a temporary directory. The block uses the resource only for its lifetime, so the resource variable can be unnamed.
record Span(String name, List<String> log) implements AutoCloseable {
Span {
log.add("start " + name);
}
@Override
public void close() {
log.add("end " + name);
}
}
List<String> log = new ArrayList<>();
try (var _ = new Span("load-playlist", log)) {
log.add("loading");
}
String trace = log.toString(); // "[start load-playlist, loading, end load-playlist]"
The resource is still closed at the end of the block, in the same way as with a named variable, as the last line of the trace shows. The try-with-resources guide covers the closing order.
3. Unnamed Patterns and Pattern Variables
In pattern matching, _ removes the variables that a record pattern would otherwise force us to declare. Before Java 22, a pattern for a record with five components needed five names even when the code used one of them.
The following example models the media types of a music app as a sealed interface with three records.
sealed interface Media permits Song, Podcast, Audiobook {}
record Song(String title, String artist, int seconds) implements Media {}
record Podcast(String show, int episode, int seconds) implements Media {}
record Audiobook(String title, String narrator, int seconds) implements Media {}
The unnamed pattern _ stands for a whole component, and the unnamed pattern variable in Podcast _ matches the type without binding the object.
static String headline(Media media) {
return switch (media) {
case Song(var title, var artist, _) -> title + " by " + artist;
case Podcast(var show, var episode, _) -> show + " #" + episode;
case Audiobook(var title, _, _) -> title + " (audiobook)";
};
}
String song = headline(new Song("Hello", "Adele", 295)); // "Hello by Adele"
String podcast = headline(new Podcast("Java Talk", 12, 1800)); // "Java Talk #12"
String book = headline(new Audiobook("Dune", "Simon", 75600)); // "Dune (audiobook)"
3.1. Several Patterns in One Case Label
A case label can list several patterns separated by commas, and it matches when any of them matches. Because only one branch of the list can match, no pattern in the list may declare a variable, so every pattern must be unnamed.
static boolean isLongForm(Media media) {
return switch (media) {
case Song _ -> false;
case Podcast _, Audiobook _ -> true;
};
}
boolean longForm = isLongForm(new Podcast("Java Talk", 13, 2400)); // true
boolean shortForm = isLongForm(new Song("Hello", "Adele", 295)); // false
A label with several patterns can have one when guard for the whole label, such as case Podcast _, Audiobook _ when premium ->. Writing case Song s, Podcast p is a compile error, because the variable s would not be set when the value is a podcast.
4. Where _ Is a Compile Error
Unnamed variables are limited to declarations that are local to a method, so _ cannot change the API of a class. It is also never an expression, so we cannot read it.
// does not compile: underscore not allowed here
class Player {
private int _; // field
void play(String _) {} // method parameter
}
// does not compile: underscore not allowed here
static boolean isMedia(Object o) {
return o instanceof _; // top-level unnamed pattern
}
// does not compile: underscore not allowed here
static int twice() {
int _ = 5;
return _ * 2; // reading an unnamed variable
}
Each of these lines makes javac 25 report the same short message.
Player.java:2: error: underscore not allowed here
private int _; // field
^
A method parameter cannot be unnamed even when the body ignores it, because parameter names belong to the method signature, appear in Javadoc and can be read through reflection. A top-level case _ is not allowed either, and we write default for a catch-all case.
5. A Real-World Example with Unnamed Variables
A music service imports play counts from a CSV file with the columns user, track and plays. The import needs only the track and the plays, skips lines with a bad number, and counts how many lines it skipped.
record PlayRow(String user, String track, String plays) {}
static Map<String, Integer> importPlays(List<String> lines) {
Map<String, Integer> totals = new TreeMap<>();
for (String line : lines) {
String[] cells = line.split(",");
PlayRow row = new PlayRow(cells[0], cells[1], cells[2]);
if (row instanceof PlayRow(_, var track, var plays)) {
try {
totals.merge(track, Integer.parseInt(plays.strip()), Integer::sum);
} catch (NumberFormatException _) {
totals.merge("skipped", 1, Integer::sum);
}
}
}
return totals;
}
List<String> csv = List.of("anna,Hello,3", "bob,Hello,2", "carl,Skyfall,x", "dina,Skyfall,4");
Map<String, Integer> imported = importPlays(csv); // {Hello=5, Skyfall=4, skipped=1}
The user column is matched with _, and the catch block ignores the exception object because the row is counted, not logged. Anyone who reads the method sees which values the import uses without reading the whole body.
6. When an Unused Variable Is a Design Problem
An unused variable is sometimes a sign that the code should change, and replacing it with _ would hide that sign. We check the following cases before we write _.
- A method parameter that no implementation uses can be removed from the method signature. The _ name is not allowed there anyway, so we have to fix the API instead.
- An ignored return value from a method that reports success, such as Files.deleteIfExists() or Lock.tryLock(), can hide a failure. We handle the result instead of declaring it as _.
- A swallowed exception in catch (Exception _) loses the stack trace. The unnamed form fits only when the exception type alone tells us what happened, as with NumberFormatException during parsing.
- A lambda that ignores all its parameters, such as (_, _) -> 0, often means a simpler functional interface such as Supplier fits better.
7. Unnamed Variables and Patterns FAQs
Java versions, method parameters and legacy code that used _ as a name are the points to check before adopting the feature.
7.1. Which Java version supports unnamed variables?
Java 22 and later, without flags. Java 21 has the same feature as a preview, so we compile with javac –release 21 –enable-preview and run with java –enable-preview.
7.2. Can we use _ as a method parameter name?
No. Method and constructor parameters, fields and record components cannot be unnamed, and javac rejects them with a compile error. Only lambda parameters, exception parameters and local declarations can use _.
7.3. Can we declare _ more than once in the same scope?
Yes. An unnamed variable does not introduce a name, so String _ = queue.poll(); can appear twice in the same block, and a lambda can have (_, _) as its parameters.
7.4. Can we read the value of an unnamed variable?
No. An unnamed variable is not an expression, so return _; or _ + 1 does not compile. If the code needs the value, it needs a real name.
7.5. Does old code that uses _ as a name still compile?
No, and it has not compiled since Java 9. Code from Java 8 or earlier that names a variable _ must rename it, for example to ignored, before it builds on a current JDK. The Java features by version page lists other changes between those releases.
8. Conclusion
The character _ marks a variable or a pattern that the code does not use. Unnamed variables fit exception parameters, lambda parameters, loop variables, try-with-resources and local variables, and unnamed patterns remove the names of record components we do not need in pattern matching.
The feature is final in Java 22 and later. It cannot be used for fields or method parameters, it cannot be read, and it should not hide an ignored result or a swallowed exception that deserves real handling.
9. References
- JEP 456, Unnamed Variables and Patterns
- JEP 443, Unnamed Patterns and Variables (Preview)
- Oracle, Unnamed Variables and Patterns
- JLS 25, Declarations
Happy Learning !!