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 Optional
s 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.