Java wird immer wieder nachgesagt, es sei zu episch. An vielen Stellen ist Boilerplate-Code notwendig, der oft wenig oder nichts zur eigentlichen Logik oder Funktionalität beiträgt.

Über die Jahre wurde vieles verbessert, was zu besser lesbarem, verständlichem und damit wartbarerem Code geführt hat: Der Diamond-Operator <>, lokale Variablen mit var, records, switch expressions, um nur einige zu nennen.

Mit Java 22 wird diese Reihe nun weitergeführt: Ungenutzte Variablen und Record-Teile können durch einen Unterstrich _ ersetzt werden, wodurch der Code kompakter und besser lesbar wird.

Außerdem ist es dem Java-Compiler durch diese Markierung einer Variablen oder eines Record-Teils als unbenutzbar möglich, jegliche Zugriffe darauf zu verhindern, sowohl schreibende als auch lesende.

Das Feature wurde mit Java 21 unter dem Namen JEP 443: Unnamed Patterns and Variables als Preview eingeführt und in Java 22 als JEP 456: Unnamed Variables & Patterns finalisiert.

Unnamed Variables

Bisher mussten Variablen immer deklariert werden, selbst wenn sie überhaupt nicht benötigt wurden, wie hier zum Beispiel die Variable ex:

try {
  Person p = personRepo.findById(id);
  return Optional.ofNullable(p);
} catch (IOException ex) {
  return Optional.empty();
}

JEP 456 ermöglicht es nun, solche Variablen durch einen Unterstrich _ zu ersetzen.

try {
    // …
} catch (IOException _) {
  return Optional.empty();
}

Aus dem nicht benötigten (und nicht verwendeten) Parameter ex wird eine Unnamed Variable und er verschwindet aus dem Quelltext. Man erkennt nun sofort, dass der catch-Block nur für die Rückgabe eines leeren Optionals relevant ist, aber die Exception selbst nicht weiter ausgewertet wird.
Auch wenn die Einsparung hier minimal erscheinen mag, führt _ zu einem kompakteren und fokussierteren Code.

Von langer Hand geplant

Die Einführung von _ wurde schon seit geraumer Zeit vorbereitet. _ als Bezeichner führte bereits in Java 8 zu einer Compilerwarnung, ab ab Java 9 dann zu einem Compilerfehler.

Ansonsten ist der Unterstrich aber weiterhin erlaubt: Als Teil eines Bezeichners mit zwei oder mehr Zeichen

int _number = 123;

und in Zahlenliteralen zur Verbesserung der Lesbarkeit

int distance = 12_000_000;

Unnamed Pattern Variables

Neben lokalen Variablen lassen sich auch ungenutzte Variablen in Record Patterns durch _ ersetzen.

Zu Erinnerung: Die mit Java 21 eingeführten Features Record Patterns und Pattern Matching for switch ermöglichen es, in switch-Konstrukten auf Teile eines Records zuzugreifen, und zwar ohne störende Casts und Accessor-Aufrufe.

Schauen wir auf diese Methode, die den Flächeninhalt einer Shape berechnet:

public double calculateAreaOld(Shape shape) {
  if (shape instanceof Point) {
    return 0.0;
  } else if (shape instanceof Square) {
    Square s = (Square)shape;
    int sideLength = s.sideLength();
    return s * s;
  } else if (shape instanceof ColoredSquare) {
    ColoredSquare cs = (ColoredSquare) shape;
    Point p = cs.getUpperLeft();
    return p.sideLength() * p.sideLength();
  }
}

 

Mit Record Patterns können wir auf die einzelnen Attribute der Records direkt zugreifen:

public double calculateAreaRecordPatterns(Shape shape) {
  return switch (shape) {
    case Point(int x, int y) -> 0;
    case Square(Point p, int s) -> s * s;
    case ColoredSquare(Square(Point p, int s), Color c) -> s * s;
  };
}

Unnamed Pattern Variables ermöglichen es uns nun, die unwichtigen Variablen wegzulassen:

public double calculateAreaUnnamedPatternVariable(Shape shape) {
  return switch (shape) {
    case Point(int _, int _) -> 0;
    case Square(Point _, int s) -> s * s;
    case ColoredSquare(Square(Point _, int s), Color _) -> s * s;
  };
}

Unnamed Patterns

Schließlich können wir das Ganze noch eins weiter treiben und auch ganze Teile von Record Patterns weglassen und durch _ ersetzen, wenn sie für den weiteren Ablauf überflüssig sind.

Hier ermöglichen es uns Unnamed Patterns, den Typ und den Namen wegzulassen:

public double calculateAreaUnnamedPattern(Shape shape) {
  return switch (shape) {
    case Point _ -> 0;
    case Square(_, int s) -> s * s;
    case ColoredSquare(Square(_, int s), _) -> s * s;
  };
}

Wo noch?

Unbenannte Variablen können auch an anderen Stellen verwendet werden:

  • in einer lokalen Variablendeklaration in einem Block
  • in der Ressourcenangabe in try-with-resources
  • im Schleifenkopf einer einfachen oder erweiterten for-Schleife
    for (var _ : names) {
      System.out.println("Processing ...");
    }
  •  als formaler Parameter eines Lambdaausdrucks
    names.forEach(_ -> System.out.println("Processing ..."));
  • als Exceptionparameter in einem catch-Block

Was bringt’s?

Wir haben gesehen, dass durch Einsatz des Unterstrichs _ der Code deutlich leichter lesbar und verständlicher wird, da unnötige Informationen entfernt werden und nicht den Blick auf das eigentlich Relevante verstellen.

Der Unterstrich _ verdeutlicht, dass ein Wert nicht für die Verwendung vorgesehen ist, und dementsprechend verhindert der Compiler auch versehentliche Schreibzugriffe, die er stattdessen mit einer Fehlermeldung quittiert.

Unnamed Variables & Patterns sind ein weiterer großer Schritt zu besserer Lesbarkeit, weniger Boilerplatecode und insgesamt kompakterem und verständlicherem Code.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert