Definition
- Unnamed variable –
_declares a variable binding that is intentionally not used; the compiler and readers know it is ignored on purpose. - Not a variable – you cannot read from
_; attempting to use it in an expression causes a compile error. - Multiple allowed – multiple
_bindings in the same scope do not conflict, unlike regular variable names. - Use cases –
catch (IOException _),for (var _ : list),(_, value) -> process(value),case Trade(var _, var _, var amount, var _),try (var _ = resource). - Final in Java 22 – JEP 456.
When the bank processes a transaction, sometimes it needs only the amount, not the customer name. Before, the form required writing a name in every field even if you would never look at it, and the quality inspector flagged the unused field. With _, you write "intentionally blank" in the name field. No warning, clear intent.
Understanding the Concepts
What are unnamed variables and patterns?
The underscore _ is not a real variable name. You cannot read from it. It signals to the compiler and future readers that this binding is deliberately ignored. You can use _ multiple times in the same scope without conflict.
Use case 1: Catch blocks
try {
tradeService.settle(trade);
} catch (SettlementException _) {
// We handle the error but do not inspect the exception object
log.error("Settlement failed for trade {}", trade.getTradeId());
trade.setStatus("FAILED");
}
Use case 2: Enhanced for-loops
// Count iterations without using the element
int count = 0;
for (var _ : trades) {
count++;
}
Use case 3: Lambda parameters
// Map.forEach: we only need the value, not the key
tradesBySymbol.forEach((_, trades) -> {
log.info("Processing {} trades", trades.size());
});
Use case 4: Record pattern bindings
record Trade(String tradeId, String symbol, java.math.BigDecimal amount, String status) {}
// We only need the amount: ignore tradeId, symbol, and status
if (obj instanceof Trade(var _, var _, var amount, var _)) {
processFee(amount);
}
// In switch:
String classify(Object obj) {
return switch (obj) {
case Trade(var _, var _, var amount, var _) when amount.compareTo(limit) > 0
-> "LARGE TRADE";
case Trade(var _, var _, var amount, var _)
-> "NORMAL TRADE";
default -> "UNKNOWN";
};
}
Use case 5: Try-with-resources
// Resource is held for its scope but never referenced
try (var _ = openConnection()) {
// Connection is open for the duration of this block
processAll();
}
// Connection closed automatically
Why can multiple _ appear in the same scope?
// Three unnamed bindings in one pattern: no conflict
case Trade(var _, var _, var amount, var _) -> process(amount);
// Regular variables would conflict:
// case Trade(var a, var a, var amount, var a) → COMPILE ERROR: duplicate variable 'a'
What are common mistakes?
// WRONG: trying to read from _
catch (IOException _) {
log.error(_.getMessage()); // COMPILE ERROR: cannot read from _
}
// WRONG: using _ as a regular variable name (Java 9+ reserved)
String _ = "hello"; // COMPILE ERROR since Java 9
What should a senior engineer know?
_was a valid identifier in Java 8 and earlier. It was reserved as of Java 9 and given this new purpose in Java 22.- Unnamed variables are purely about code cleanliness and intent documentation. They do not change runtime behavior.
- In record patterns where a record has 5+ components,
_keeps the pattern readable by showing which fields matter.
What are common interview follow-up questions?
- Is
_a keyword? (It is a reserved name for unnamed variables, not a traditional keyword.) - Can you use
_in a method parameter? (Yes, as of Java 22:void handle(Event _).) - Does
_have a value? (No. It is a discard. You cannot read it.)
Visual Overview
Unnamed Variables Usage: catch (IOException _) → ignore exception variable for (var _ : list) → ignore loop element (_, value) -> use(value) → ignore lambda param case Trade(_, _, amt, _) → ignore record fields try (var _ = resource) → ignore resource variable All signal: "I know this exists, I do not need it." Cannot read from _. Multiple _ allowed in same scope.
Key Takeaway
Unnamed variables (_, JEP 456, Java 22) let you declare a binding that you intentionally do not use. The underscore signals both to the compiler and to future readers that this variable is deliberately ignored. Before JEP 456, every catch block, for-loop, and lambda parameter required a named variable even if it was never read, creating IDE warnings and noise.
Five use cases cover the common scenarios: catch (IOException _) when you handle the error without inspecting the exception, for (var _ : list) when you count iterations, (_, value) -> process(value) when a lambda ignores one parameter, case Trade(var _, var _, var amount, var _) when destructuring records and only needing some fields, and try (var _ = resource) when holding a resource for its scope without referencing it.
The key rules: _ is not a real variable. You cannot read from it (compile error if you try). Multiple _ bindings in the same scope do not conflict, which is essential for record patterns where you might ignore 4 out of 5 components. The feature is purely about code cleanliness and intent documentation.
Remember one sentence: use _ for intentionally unused variables in catch, for, lambda, and pattern bindings to eliminate warnings and document intent.
---