Java 21 Unnamed Variables and Patterns

August 2, 20265 min readUpdated 8/20/2026

An unnamed variable is written _ and says "something goes here and I will not use it". It removes the invented names — ignored, unused, e2 — that clutter code where a value is structurally required but genuinely irrelevant.

A note on versions

This was a preview feature in Java 21, not a standard one. On Java 21 the examples below need --enable-preview; from Java 22 onwards they compile normally. The examples here are compiled against Java 25, where they are standard.

# Java 21 only
javac --release 21 --enable-preview Example.java
java --enable-preview Example

# Java 22 and later
javac Example.java

Worth knowing which side of that line your build is on before adopting the syntax, because a preview feature also requires the flag at runtime and pins the class file to that exact release.

Unused loop variables

class Demo {
    int countItems(List<String> items) {
        int total = 0;
        for (String _ : items) {        // the element is irrelevant; we are counting
            total++;
        }
        return total;
    }
}

The limit is worth stating immediately: _ declares a variable you cannot refer to. A classic for loop reads and updates its counter, so it cannot be unnamed — this does not compile, on any version:

class Demo {
    void repeat(int times) {
        for (int _ = 0; _ < times; _++) {   // error: underscore not allowed here
        }
    }
}

Only variables you genuinely never mention qualify.

Unused catch parameters

class Demo {
    boolean isNumber(String text) {
        try {
            Integer.parseInt(text);
            return true;
        } catch (NumberFormatException _) {     // the exception carries nothing we need
            return false;
        }
    }
}

This is the most defensible use in the whole feature. The old version named a variable e and never touched it, which looks exactly like the swallowed exception that post warns about. _ makes the difference visible: this is a deliberate decision that the exception carries no information, not an oversight.

Unused lambda parameters

class Demo {
    void run() {
        Map<String, Integer> scores = new HashMap<>(Map.of("Ana", 30));

        // Only the key matters
        scores.forEach((name, _) -> System.out.println(name));

        // Only the value matters
        scores.forEach((_, score) -> System.out.println(score));

        // computeIfAbsent hands you the key; sometimes you do not need it
        scores.computeIfAbsent("Bo", _ -> 0);
    }
}

Unnamed patterns

The other half of the feature, and the one that composes best with record patterns. A _ in a pattern matches a component without binding it:

record Point(int x, int y) { }
record Line(Point start, Point end) { }

class Demo {
    String describe(Object value) {
        return switch (value) {
            // Only the x coordinates matter here
            case Line(Point(int x1, _), Point(int x2, _)) when x1 == x2 -> "vertical";
            case Line(Point(_, int y1), Point(_, int y2)) when y1 == y2 -> "horizontal";
            case Line _ -> "diagonal";           // matched, contents irrelevant
            default -> "not a line";
        };
    }
}

Without _, that first case reads Line(Point(int x1, int y1), Point(int x2, int y2)) and declares two variables nobody uses. The version above says which two of the four components the decision depends on, which is information the reader wants.

A type pattern can be unnamed too — case Line _ tests the type and binds nothing.

The rules

  • You cannot read it. Referring to _ is a compile error, which is what makes the intent enforceable rather than a convention.
  • You can declare several in one scope. Two variables named _ do not clash, because neither can be referred to. This is the one place Java relaxes its no-duplicate-names rule.
  • It works for local variables, catch parameters, lambda parameters, patterns, and the resource variable in a try-with-resources.
  • It does not work for fields, method parameters, or anything that forms part of a public signature.
class Demo {
    void multiple(List<String> a, List<String> b) {
        for (String _ : a) {
            for (String _ : b) {          // legal — neither can be referenced
                System.out.println("pair");
            }
        }
    }

    // A resource you must close but never use
    void resource(Path path) throws IOException {
        try (var _ = Files.newBufferedReader(path)) {
            System.out.println("opened and closed");
        }
    }
}

What it is not

Two things _ resembles in other languages and is not in Java.

It is not a wildcard you can assign to repeatedly. In some languages _ is a sink you can write to as often as you like. In Java it is a declaration, and each one declares a separate variable that happens to be unnameable — there is no shared bucket.

It is not a way to ignore a return value. Java has never required you to use a return value, so there is nothing to suppress:

class Demo {
    void run(List<String> names) {
        // var _ = names.size();      // legal, but pointless
        names.size();                 // already fine — Java does not warn on an unused result
    }
}

The one place the assignment form earns its keep is try-with-resources, where the language requires a variable in order to close the resource and you have no use for it — the example above.

Why the underscore was available

A small piece of history that explains the odd delay. _ was a perfectly ordinary identifier in early Java — people used it as a variable name. Java 8 made it a warning, Java 9 made it a compile error, and that deliberate deprecation is what freed the character for this use a decade later.

So if you meet _ as a real variable name, you are looking at code written for Java 7 or earlier, and it will not compile on anything modern.

Worth using?

It is a small feature and it does not change how you design anything. Two uses genuinely improve code: the catch parameter, because it distinguishes a deliberate ignore from a swallowed exception, and unnamed patterns, because they say which components a decision actually depends on.

The rest is tidiness. Use it where it removes a lie — a name suggesting a value matters when it does not — and do not go hunting for opportunities.

Next

Other Java 21 improvements is next.