unnamed variables and patternsunnamed variables and patterns
HappyCoders Glasses

Unnamed Variables and Patterns in Java

Sven Woltmann
Sven Woltmann
Last update: November 27, 2024

In this article, you will learn:

  • What is an unnamed variable, and what purpose does it serve?
  • What are unnamed patterns and unnamed pattern variables, and what purpose do they serve?
  • How can you write switch expressions more concisely with unnamed pattern variables?

Unnamed variables and patterns were introduced as a preview feature in Java 21 as part of Project Amber. They were finalized in Java 22.

Unnamed Variables

It often happens that we have to define a variable that we don’t need later. Here are two examples that probably most of you know:

Example 1: Exceptions – here, we don’t use e:

try {
  int number = Integer.parseInt(string);
} catch (NumberFormatException e) {
  System.err.println("Not a number");
}Code language: Java (java)

Example 2: Map.computeIfAbsent() – this time, we don’t use k:

map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);Code language: Java (java)

As of Java 21, we no longer have to name such variables but may (as has long been common in other programming languages) use the underscore (_) instead:

Here is the exception example with an unnamed variable:

try {
  int number = Integer.parseInt(string);
} catch (NumberFormatException _) {
  System.err.println("Not a number");
}Code language: Java (java)

And the computeIfAbsent() example:

map.computeIfAbsent(key, _ -> new ArrayList<>()).add(value);Code language: Java (java)

With an exception, one may argue about the pros and cons of an unnamed variable. We are used to naming an exception with “e” or letting our IDE do that automatically.

With computeIfAbsent(), on the other hand, I was always worried about how to name the variable I didn’t need. Sometimes it became a k (for “key”), sometimes an ignored, and sometimes a __ (double underscore²). This is where the unnamed variable is a big help.

² The single underscore is no longer allowed since Java 9 in preparation for this very feature.

Unnamed Patterns and Pattern Variables

By the way, the feature is not called “Unnamed Variables,” but “Unnamed Patterns and Variables” and thus has a lot more to offer – namely in combination with the features Record Patterns and Pattern Matching for Switch, which have been finalized in this Java version.

In the following, I have slightly modified the example from the “Record Patterns” section. The variable y is no longer used in this variant:

if (object instanceof Position(int x, int y)) {
  System.out.println("object is a position, x = " + x);
}Code language: Java (java)

Again, we can replace y with an underscore:

if (object instanceof Position(int x, int _)) {
  System.out.println("object is a position, x = " + x);
}Code language: Java (java)

This is called an “unnamed pattern variable.”

We can even go one step further and replace the entire partial pattern int y with an underscore:

if (object instanceof Position(int x, _)) {
  System.out.println("object is a position, x = " + x);
}Code language: Java (java)

This is called an “unnamed pattern.”

In the previous example, this doesn’t have much effect; however, using nested patterns, it can save a lot of space. Here is a modified example from the “Record Patterns” section. Here we use only the variables x1 and y1, while x2 and y2 are unused:

if (object instanceof Path(Position(int x1, int y1), Position(int x2, int y2))) {
  System.out.printf("object is a path starting at x = %d, y = %d%n", x1, y1));
}Code language: Java (java)

Here we can replace the complete second Position pattern with the underscore:

if (object instanceof Path(Position(int x1, int y1), _)) {
  System.out.printf("object is a path starting at x = %d, y = %d%n", x1, y1));
}Code language: Java (java)

That, after all, represents a significant improvement.

Unnamed Pattern Variables and Pattern Matching for Switch

Here is an example with unused variables in Pattern Matching for Switch:

switch (obj) {
  case Byte    b -> System.out.println("Integer number");
  case Short   s -> System.out.println("Integer number");
  case Integer i -> System.out.println("Integer number");
  case Long    l -> System.out.println("Integer number");

  case Float  f -> System.out.println("Floating point number");
  case Double d -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}Code language: Java (java)

Again, we may replace all variable names with underscores:

switch (obj) {
  case Byte    _ -> System.out.println("Integer number");
  case Short   _ -> System.out.println("Integer number");
  case Integer _ -> System.out.println("Integer number");
  case Long    _ -> System.out.println("Integer number");

  case Float  _ -> System.out.println("Floating point number");
  case Double _ -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}Code language: Java (java)

We can even go one step further and combine all cases with the same actions:

switch (obj) {
  case Byte _, Short _, Integer _, Long _ -> System.out.println("Integer number");
  case Float _, Double _                  -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}Code language: Java (java)

And this is – besides the more concise notation – another significant advantage of the unnamed variable! With named variables, this would not have been possible. The following code is not valid:

switch (obj) {
  // Not allowed!          
  case Byte b, Short s, Integer i, Long l -> System.out.println("Integer number");
  case Float f, Double d                  -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}Code language: Java (java)

This code results in the following compiler error:

error: illegal fall-through from a pattern
  case Byte b, Short s, Integer i, Long l -> System.out.println("Integer number");
               ^Code language: plaintext (plaintext)

The crucial difference is that named variables can be accessed from subsequent code, while unnamed variables may not be accessed. Since the compiler does not know which pattern will match at runtime, it also does not know which of the variables b, s, i, and l may be accessed. Therefore, it allows only one named variable per case but any number of unnamed variables.

Unnamed Patterns and Variables are defined in JDK Enhancement Proposal 456. In the JEP, you can find some more examples of using unnamed variables.

You don’t want to miss any HappyCoders.eu article and always be informed about new Java features? Then click here to sign up for the free HappyCoders newsletter.