Why is a single underscore (_) by itself no longer a valid identifier in modern Java?
answer
- Single _ alone: warning in 8, error in 9
- Java 21+: _ = unnamed/ignored variable or pattern
- _ inside names (my_var) still fine
- 1_000 numeric separators unaffected
- Unnamed _ can repeat in scope; cannot be read
basics
~10 sA lone underscore (_) used to be a legal name, but modern Java reserved it. You can no longer use just _ as a variable name; it now marks an unnamed (ignored) variable instead.
solid answer
~40 sThe underscore character is still a legal identifier character in general, but a name consisting of only a single underscore is reserved. This started as a warning in Java 8 and became a hard compile error in Java 9, because the language was reserving _ for future use. That future use arrived: Java 21 introduced unnamed variables and patterns, where a single _ means 'I intentionally don't need this value' — for example in a catch block, a lambda parameter, or a pattern you don't bind. So _ is no longer a name you can read or reference; it is a placeholder telling the compiler and readers the value is deliberately discarded. Underscores remain fine inside longer names like my_var or as digit separators in numeric literals like 1_000.
code
java · 16 lines// Underscore inside a name: fine
int first_name_length = 5;
// Numeric literal separators: fine (Java 7+)
int million = 1_000_000;
// Bare _ as a normal name: COMPILE ERROR since Java 9
// int _ = 42; // not allowed
// System.out.println(_); // cannot read an unnamed binding
// Java 21+: _ means 'intentionally unnamed / ignored'
try {
Integer.parseInt("x");
} catch (NumberFormatException _) { // we don't need the exception
System.out.println("bad number");
}go deeper
Knows that you can't use just _ as a variable name in current Java.
Knows the lone _ is reserved and that underscores are still fine inside longer names and in numeric literals.
Recounts the warning(8)/error(9) timeline and explains the Java 21+ unnamed-variable meaning and its uses.
Explains the language-evolution rationale of reserving a token before assigning semantics, and the interaction with unused-variable warnings and pattern matching.
## Start with the character vs the whole name It is important to separate two things: - The **underscore character** `_` is, and remains, a perfectly legal *identifier character* — both as a starting character and a later character. So `my_var`, `_id`, `MAX_VALUE` are all fine. - A **whole identifier that is just `_`** (a single underscore, nothing else) is the special case that changed. ## The timeline 1. **Before Java 8:** `_` alone was an ordinary, legal identifier. People occasionally used it as a throwaway variable name, e.g. `(_) -> doSomething()` or a loop variable they ignored. 2. **Java 8 (2014):** The compiler began emitting a **warning** when you used `_` as an identifier, with a message that it would be reserved in a future release. This was a heads-up, not yet an error. 3. **Java 9 (2017):** Using `_` alone as an identifier became a **compile-time error**. The single underscore was now reserved by the language, deliberately kept free so it could be given a meaning later. 4. **Java 21 (2023):** That meaning arrived as **unnamed variables and unnamed patterns** (previewed in Java 21, finalized in Java 22). A single `_` now means 'an unnamed binding' — you are telling the compiler you intentionally do not use this value. ## What unnamed `_` is for The motivation is *intent*. Sometimes you must declare something the language requires, but you genuinely don't care about its value, and naming it (a) wastes a name, (b) can trigger 'unused variable' warnings, and (c) hides the fact that the value is deliberately ignored. The unnamed `_` says 'ignored on purpose.' Common uses: - A **catch** parameter you don't inspect: `catch (NumberFormatException _) { return DEFAULT; }` - A **lambda** parameter you don't use: `map.forEach((key, _) -> process(key));` - A **for-each** loop variable you only count: `for (var _ : list) { count++; }` - An **unnamed pattern** in a record pattern: `if (obj instanceof Point(int x, _)) { use(x); }` Because `_` is unnamed, you **cannot read it** — there is no variable to reference. You can also use `_` more than once in the same scope without a 'duplicate variable' error, since none of them introduce a name. ## Why reserve it rather than overload it gradually? Languages reserve a token *before* assigning meaning so that existing code can be migrated and so the new semantics aren't ambiguous with old uses. By first warning (Java 8) then erroring (Java 9), Java gave developers years to stop using `_` as a real name, clearing the runway for the Java 21 feature without breaking the new semantics. ## What still works with underscores Don't overcorrect. These are all still legal and unaffected: - Underscores **inside** identifiers: `first_name`, `_internal`, `CONSTANT_CASE`. - Underscores as **numeric literal separators**: `1_000_000`, `0xFF_FF`, `0b1010_1010` (a separate Java 7 feature for readability). Only the **bare, single `_`** as a name changed. ## Summary `_` the character is fine; `_` as a complete identifier was warned in Java 8, made an error in Java 9 (reserved), and given the meaning 'unnamed/ignored binding' in Java 21+. It is now a deliberate discard marker, not a readable name.
- Is my_var still a legal identifier?Yes. The restriction is only on a single underscore used alone as a name. Underscores inside a longer identifier are unaffected.
- What does a single _ mean in Java 21+?It denotes an unnamed (ignored) variable or pattern binding — a deliberate discard you cannot reference, useful for unused catch/lambda/loop variables and unmatched pattern components.
saying these in an interview costs you the question
- Saying underscore is now illegal everywhere (only the lone _ as a name changed)
- Confusing the bare _ rule with numeric literal separators (1_000)
- Thinking you can still read the value of an unnamed _
- Claiming _ alone was always an error (it was legal before Java 9)