unnamed variables and patternsunnamed variables and patterns
HappyCoders Glasses

Unbenannte Variablen und Patterns in Java

Sven Woltmann
Sven Woltmann
Aktualisiert: 19. März 2024

In diesem Artikel erfährst du:

  • Was ist eine unbenannte Variable, und welchen Zweck erfüllt sie?
  • Was sind unbenannte Patterns und unbenannte Pattern-Variablen, und welchen Zweck erfüllen sie?
  • Wie kann man Switch-Ausdrücke mit unbenannten Pattern-Variablen prägnanter schreiben?

Unbenannte Variablen und Patterns wurden in Java 21 im Rahmen von Project Amber als Preview-Feature eingeführt. In Java 22 wurden sie finalisiert.

Unbenannte Variablen

Oft kommt es vor, dass wir eine Variable definieren müssen, die wir gar nicht benötigen. Hier zwei Beispiele, die wahrscheinlich die meisten von euch kennen:

Beispiel 1: Exceptions – hier wird e nicht verwendet:

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

Beispiel 2: Map.computeIfAbsent() – hier wird k nicht verwendet:

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

Ab Java 22 (bzw. ab Java 21 mit aktivierten Preview-Features) müssen wir solche Variablen nicht mehr benennen, sondern dürfen (wie in anderen Programmiersprachen lange üblich) stattdessen den Unterstrich (_) verwenden:

Hier das Exception-Beispiel mit unbenannter Variable:

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

Und das computeIfAbsent()-Beispiel:

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

Bei einer Exception mag man sich über das Für und Wider einer unbenannten Variable streiten. Wir sind daran gewöhnt eine Exception mit „e” zu bezeichnen, bzw. unsere IDE das automatisch machen zu lassen.

Bei computeIfAbsent() hingegen habe ich mir immer Gedanken darüber gemacht, wie ich die nicht benötigte Variable benennen soll. Manchmal wurde es ein k (für „key”), manchmal ein ignored und manchmal ein __ (doppelter Unterstrich²). Hier ist die unbenannte Variable eine große Hilfe.

² Der einfache Unterstrich war seit Java 9 in Vorbereitung auf genau dieses Feature nicht mehr erlaubt.

Unbenannte Patterns und Pattern-Variablen

Das Feature heißt allerdings nicht „Unnamed Variables”, sondern „Unnamed Patterns and Variables” und hat somit noch einiges mehr zu bieten – und zwar bei den in Java 21 finalisierten Features Record Patterns und Pattern Matching for Switch.

Die Variable y wird im „then-Block” des folgenden Code-Beispiels nicht benötigt:

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

Auch hier können wir deswegen y durch einen Unterstrich ersetzen:

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

Das nennt sich dann „unbenannte Pattern-Variable”.

Wir können sogar noch einen Schritt weiter gehen und das komplette Teil-Pattern int y durch einen Unterstrich ersetzen:

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

Das nennt sich dann „unbenanntes Pattern”.

Im vorigen Beispiel hat das noch keine große Auswirkung; bei verschachtelten Patterns hingegen lässt sich damit eine Menge Platz sparen. Im folgenden Beispiel verwenden wir nur die Variablen x1 und y1, während x2 und y2 unbenutzt sind:

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-Sprache: Java (java)

Hier können wir das komplette zweite Position-Pattern durch den Unterstrich ersetzen:

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-Sprache: Java (java)

Das stellt doch eine deutliche Verbesserung dar!

Unbenannte Pattern-Variablen und Pattern Matching for Switch

Hier ein Beispiel mit nicht verwendeten Variablen bei 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-Sprache: Java (java)

Auch hier dürfen wir alle Variablennamen durch Unterstriche ersetzen:

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-Sprache: Java (java)

Wir können sogar noch einen Schritt weitergehen und alle Fälle mit gleichen Aktionen zusammenfassen:

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-Sprache: Java (java)

Und das ist – neben der prägnanteren Schreibweise – der zweite große Vorteil der unbenannten Pattern-Variable! Mit benannten Variablen wäre das nämlich nicht möglich gewesen. Der folgende Code ist nicht gültig:

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-Sprache: Java (java)

Dieser Code führt zu folgendem Compilerfehler:

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

Der entscheidende Unterschied ist, dass auf benannte Variablen von nachfolgendem Code aus zugegriffen werden kann, während auf unbenannte Variablen nicht zugegriffen werden darf. Da der Compiler nicht weiß, welches Pattern zur Laufzeit matchen wird, weiß er auch nicht, auf welche der Variablen b, s, i und l zugegriffen werden darf. Daher lässt er pro Fall nur eine benannte Variable, aber beliebig viele unbenannte Variablen zu.

Unnamed Variables & Patterns werden in JDK Enhancement Proposal 456 definiert. Dort findest du noch ein paar weitere Beispiele für den Einsatz unbenannter Variablen.

Du willst über alle neue Java-Features auf dem Laufenden sein? Dann klicke hier, um dich für den HappyCoders-Newsletter anzumelden.