Switch Expressions in Java 12

Java 12 bringt einige interessante neue Features, darunter auch umfassende Erweiterungen für die Kontrollstruktur switch. Spezifisch befassen wir uns im Folgenden mit bisherigen Problemen und wie diese unter anderem durch die neue Switch-Expression in Java 12 gelöst werden.

Das jetzige Switch-Statement und seine Macken

Im Laufe des Artikels werden wir anhand des folgenden Anwendungsfalls die Neuerungen von Switch näher betrachten.

public String describeInt(int i) {
  String str = "not set";
  switch(i) {
    case 1:
      str = "one";
    case 2: 
      str = "two";
      break;
    case 3:
      str = "three";
      break;
  }
  return str;
}

Das klassische Switch-Statement ist relativ klobig und wird, sobald es sich um komplexere Codeabschnitte handelt, schnell unübersichtlich. Da kann es durchaus vorkommen, dass man ein break übersieht oder vergisst, einen Fall abzufangen. Folgend eine kurze Auflistung der Hauptprobleme, welche im Codebeispiel enthalten sind:

  1. Unabsichtlicher Fallthrough: Vergisst man ein Break-Statement innerhalb einer der Cases, so “fällt” das Programm bei der Ausführung weiter in den nächsten Case, auch wenn dieser nicht durch das Switch selbst getriggert wurde. Im Beispiel wird in Case 1 auch Case 2 ausgeführt.
  2. Keine garantierte Case Abdeckung: Sollte man beim Erstellen des Switch vergessen, einen Case abzudecken, tritt kein Compilerfehler auf. Sollte dieser Case eintreten und kein Default-Case definiert sein, wird das Switch-Statement einfach übergangen. Im Beispiel werden Cases von i < 1 und i > 3 nicht abgedeckt.
  3. Boiler-Plate-Code: Wie bereits erwähnt, wirkt das Statement sehr klobig. Dies rührt unter anderem daher, dass oft nur unterschiedliche Werte immer derselben Variablen zugewiesen werden. Abgeschlossen wird dann jeder Case mit einem break;.
  4. Kein Scope: Eine weitere Unannehmlichkeit ist, dass im traditionellen Switch-Statement lediglich ein globaler Scope existiert. Folglich müssen Variablen, die in den einzelnen Pfaden deklariert werden, alle switch-intern unterschiedlich benannt sein. Dies führt schnell zu Magic-Number-Variablen wie varCase1 und varCase2.

Ein bisher beliebter Lösungsansatz für Fallthrough und Case Abdeckung in diesem generellen Anwendungsfall, wäre beispielsweise das Deklarieren der zuzuweisenden Variable als final.

Fortschritt in Java 12

Mehrere Case-Labels

Ab Java 12 können nun auch mehrere Cases in einem Pfad abgefangen werden. Dies erspart uns die redundante Deklaration von Cases, welche per Fallthrough den gleichen Code ausführen würden. Dies lässt sich nun deutlich kompakter schreiben:

public String describeInt(int i) {
  String str = "not set"; 
  switch (i) {
     case 1, 2: 
       str = "one or two";
       break;
     case 3: 
       str = "three";
       break; 
  }
  return str;
}

Die Pfeil-Syntax

In Java 12 wird eine neue Syntaxvariante eingeführt, die den Lambda-Ausdrücken ähnelt.

public String describeInt(int i) {
  String str = "not set"; 
  switch (i) {
    case 1, 2 -> str = "one or two";
    case 3 -> str = "three";
  }
  return str;
}

Diese neue Syntax ermöglicht es auf den ersten Blick, den repetitiven Code der Break-Syntax zu vermeiden, was den Code schlanker und übersichtlicher macht. Darüber hinaus werden durch dieses neue Feature noch zwei weitere Macken von Switch ausgeräumt.

  1. Ein unabsichtlicher Fallthrough ist nun nicht mehr möglich. Es wird immer nur genau ein Statement auf der rechten Seite des Pfeils ausgeführt.
  2. Sobald mehr als ein Statement auf der rechten Seite benötigt wird, muss der Code innerhalb eines Blocks stehen. Damit wird ein eigener Scope geschaffen, welcher eine kollisionsfreie Variablendeklaration ermöglicht.

Die Switch-Expression

In vielen Fällen, in denen das Switch-Statement bisher benutzt wurde, handelt es sich um Bestrebungen, die funktionalen Eigenschaften einer Expression nachzuahmen, sprich die konditionale Zuweisung von verschiedenen Werten zu einer Variablen. Diese Nachbildung, auch im Anwendungsfall vertreten, ist fehleranfällig und nicht sehr intuitiv.
In Java 12 kommt eine Antwort auf dieses Problem: Die Switch-Expression!

public static String describeInt(int i) {
  return switch (i) {
    case 1, 2: 
      break "one or two";
    case 3: 
      break "three";
    default: 
      break "smaller than one or bigger than three";
  };
}

Die Switch-Expression hat als Expression gegenüber dem Statement einen Rückgabewert. Übergeben wird dieser mit einer “break-with-value”-Semantik, welche direkt auf break folgend einen Wert erwartet.

Weiterhin interessant ist, dass aufgrund ihrer Eigenschaften die Expression die letzte verbleibende Macke behebt, die das alte Switch-Statement mit sich brachte. Durch den Zwang zu einem Wert auflösbar zu sein, muss der Compiler sicherstellen, dass alle Cases abgedeckt sind. Folglich wird umfassende Case-Abdeckung garantiert.

Die Switch-Expression kann alternativ auch mit der bereits erwähnten Pfeil-Syntax benutzt werden. Diese Schreibweise verkürzt die Expression noch weiter:

public static String describeInt(int i) {
  return switch (i) {
    case 1, 2 -> "one or two";
    case 3 -> "three";
    default -> "smaller than one or more than three";
  };
}

Weiterhin…

Type Inference

Sollte eine Variable mit var gekennzeichnet sein, so versucht der Compiler, den Typ dieser Variable zu inferieren. Im Falle der Switch-Expression bedeutet dies, dass der spezifischste gemeinsame Typ aller Cases zum Typ der Variablen wird. Wird der Typ der Variablen bei ihrer Deklaration mit angegeben, muss der Rückgabetyp der Expression, sprich der spezifischste gemeinsame Typ aller Cases, diesem entsprechen.

Das geht mit Expressions nicht!

Ein “Nachteil” von Expressions ist, dass wie bereits erwähnt eine Expression letztendlich zu einem Wert aufgelöst werden muss. Aufgrund dessen ist es nicht möglich, innerhalb der Expression return oder continue zu benutzen, wie das gerne in Switch-Statements gemacht wird, um direkt aus der Methode bzw. Iteration zu springen. Es ist allerdings möglich, eine Exception zu werfen und damit die Expression frühzeitig zu beenden.

Noch in der Schwebe

Bisher unterstützte Switch byte, short, char, int (inklusive deren Wrapper-Klassen), Enums und Strings. Es wurde allerdings angedeutet, dass vielleicht bald auch float, double und long (sowie ihre Wrapper-Klassen) unterstützt werden sollen.

Selbst testen

In Entwicklerversionen einiger IDEs , wie beispielsweise Intellij IDEA 2019.1, gibt es bereits Support für diese neuen Preview-Features. Um das alles schnell selbst auszuprobieren, bleibt aber immer noch das Ausführen über die Kommandozeile. Hierzu wird ein jdk12 vorausgesetzt. Da es sich wie bereits gesagt um ein Preview-Feature handelt, müssen der CLI zusätzlich die Parameter --enable-preview und --source 12 mitgegeben werden:

java --enable-preview --source 12 YourFile.java
Short URL for this post: https://wp.me/p4nxik-3do
This entry was posted in Java Basics and tagged , . Bookmark the permalink.

2 Responses to Switch Expressions in Java 12

  1. Pingback: Java 12 Überblick | techscouting through the java news

  2. Pingback: Java 13 Überblick | techscouting through the java news

Leave a Reply