JAX Blog

Java 12 Tutorial: So funktionieren die neuen Switch Expressions

Mar 19, 2019

Java verharrte bislang beim switch-case-Konstrukt sehr bei den uralten Wurzeln aus der Anfangszeit der Programmiersprache und den Kompromissen im Sprachdesign, die C++-Entwicklern den Umstieg erleichtern sollten. Relikte wie das break und bei dessen Fehlen das Fall-Through waren wenig intuitiv und luden zu Fehlern ein. Zudem war man beim case recht eingeschränkt bei der Notation der Werte. Das alles ändert sich glücklicherweise mit Java 12. Dazu wurde die Syntax leicht modifiziert und erlaubt nun die Angabe einer Expression sowie mehrerer Werte beim case. Auf diese Weise lassen sich Fallunterscheidungen deutlich eleganter formulieren.

Die neuen Switch Expressions in Java 12

Als Beispiel für dieses neue Sprachfeature in Java 12 dient die Abbildung von Wochentagen auf deren textuelle Länge. Analysieren wir zunächst, wieso eine Erweiterung und Modifikation in der Syntax und dem Verhalten von switch sinnvoll ist. Was waren die bisherigen Schwachstellen beim switch? Zur Verdeutlichung schauen wir uns in Listing 1 an, wie man die genannte Abbildung vor Java 12 formulieren würde.

Stay tuned

Regelmäßig News zur Konferenz und der Java-Community erhalten

 

Listing 1

DayOfWeek day = DayOfWeek.FRIDAY;

int numLetters = -1;

switch (day)
{
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
    numLetters = 6;
    break;
  case TUESDAY:
    numLetters = 7;
      break;
  case THURSDAY:
  case SATURDAY:
    numLetters = 8;
    break;
  case WEDNESDAY:
    numLetters = 9;
    break;
};

 

Betrachten wir den Sourcecode kritisch. Zunächst einmal ist das gezeigte Konstrukt nicht wirklich elegant und ziemlich lang. Auch die Mehrfachangabe von Werten ist gewöhnungsbedürftig. Schlimmer noch: Es wird ein break benötigt, damit die Verarbeitung überraschungsfrei abläuft und es zu keinem Fall-Through kommt. Zudem müssen wir die (künstliche) Hilfsvariable in jedem Zweig korrekt setzen. Wie geht es also besser?

Syntaxerweiterungen mit Java 12: In Java 12 ist mit „Switch Expressions“ ein als Preview gekennzeichnetes Sprachfeature aufgenommen worden, was die Formulierung von Fallunterscheidungen deutlich erleichtert. In Listing 2 ist die intuitive Schreibweise gut ersichtlich:

Listing 2

public static void switchWithReturnAndMultipleValues();
{
  DayOfWeek day = DayOfWeek.FRIDAY;

  int numLetters = switch (day)
  {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};
}

An diesem Beispiel erkennen wir folgende syntaktischen Neuerungen: Zunächst ändert sich die Schreibweise beim case. Neben dem offensichtlichen Pfeil statt des Doppelpunkts können nun auch mehrere Werte aufgeführt werden. Bemerkenswert ist vor allem, dass wir kein break mehr benötigen: Die hinter dem Pfeil angegebenen Anweisungen werden jeweils nur spezifisch für das case ausgeführt, und es existiert bei dieser Syntax kein Fall-Through. Schließlich kann das switch nun einen Wert zurückgeben, wodurch sich die Definition von Hilfsvariablen vermeiden lässt.

Aktivierung der Java-12-Syntaxerweiterungen: Weil es sich bei den Switch Expressions leider nicht um ein finales Feature handelt, muss man beim Kompilieren und Ausführen des Programms den Kommandozeilenparameter –enable-preview angeben. Weitere Hinweise finden Sie weiter unten im Text.

 

Java 12: Syntaxvarianten bei „switch“

Während allein schon die zuvor vorgestellten Syntaxänderungen eine tolle Erweiterung darstellen, darf man sich doch an weiteren verbesserten Varianten von switch erfreuen: Zuweisungen im Lambda und break mit Rückgabewert.

Zuweisungen im Lambda: Im ersten Beispiel zu der Java-12-Syntax wurde auf der rechten Seite lediglich ein Wert zurückgegeben. Es ist aber weiterhin problemlos möglich, dort eine Zuweisung oder Methodenaufrufe vorzunehmen, selbstverständlich nach wie vor ohne die Notwendigkeit für ein break (Listing 3).

Listing 3

public static void switchAssignment()
{
  final int value = 2;
  String numericString;

  switch (value)
  {
    case 1 -> numericString = "one";
    case 2 -> numericString = "two";
    case 3 -> numericString = "three";
    default -> numericString = "N/A";
  }

  System.out.println("value:" + value + " as string: " + numericString);
}

„break“ mit Rückgabewert: Alle, die einmal eine Java-Zertifizierung machen möchten, freuen sich bestimmt über eine weitere Variante der Syntax. Kommen wir dazu nochmals auf die bisherige Syntax zurück und bilden Namen von Farben auf deren Anzahl an Buchstaben ab – hier bewusst mit einem kleinen Flüchtigkeitsfehler zur Demonstration von Fall-Through versehen (Listing 4).

Listing 4

Color color = Color.GREEN;
int numOfChars;

switch (color)
{
  case RED: numOfChars = 3; break;
  case BLUE: numOfChars = 4; break;
  case GREEN: numOfChars = 5; /* break; UPS: FALL-THROUGH */
  case YELLOW: numOfChars = 6; break;
  case ORANGE: numOfChars = 6; break;
  default: numOfChars = -1;
}

System.out.println("OLD: " + numOfChars);

In diesem Beispiel erkennt man die Nachteile der alten Syntax, die doch etwas sperrig in der Handhabung ist und ohne explizite Kennzeichnung den Fall-Through kaum ersichtlich macht. Da ist der Zwang zur Definition der künstlichen Hilfsvariable eher nur ein Schönheitsmakel. Schauen wir, wie es mit Java 12 besser geht.

Umsetzung mit Java 12: Die nachfolgend gezeigte Variante mit Java 12 ist recht nah an der alten Syntax, jedoch mit dedizierten Verbesserungen: Als eher minimale Abwandlung kann man hinter dem break einen Rückgabewert notieren. Zudem können mehrere Werte in case aufgeführt werden. Insgesamt ergibt sich damit die in Listing 5 dargestellte, besser lesbare Variante.

Listing 5

public static void switchBreakReturnsValue()
{

  Color color = Color.GREEN;

  int numOfChars = switch (color)
  {
    case RED: break 3;
    case BLUE: break 4;
    case GREEN: break 5;
    case YELLOW, ORANGE: break 6;
    default: break -1;
  };
  System.out.println("color:" + color + " ==> " + numOfChars);
}

In diesem Beispiel werden die Vorteile der Syntax deutlich. Diese ist zwar sehr ähnlich zu der bisherigen Variante, doch gibt es einige Erleichterungen und Verbesserungen. Zum einen benötigt man keine künstliche Hilfsvariable mehr, sondern kann mit break einen Wert zurückgeben, wodurch die Abarbeitung dort gestoppt wird, also ähnlich zu dem return einer Methode. Schließlich kann man nun mehrere Werte in einem case angeben, wodurch sich eine kompaktere Schreibweise erzielen lässt.

SIE LIEBEN JAVA?

Den Core-Java-Track entdecken

 

Anpassungen für Build-Tools und IDEs

Zur Demonstration der Auswirkungen von Java 12 auf Build-Tools und IDEs nutzen wir eine einfache Beispielapplikation mit folgenden Verzeichnisaufbau:

Java12Examples
|-- build.gradle
|-- pom.xml
‘-- src
  ‘-- main
    ‘-- java
      ‘-- java12
        ‘-- SwitchExample.java
 

Die Methoden der Klasse SwitchExample haben wir bereits in den vorherigen Abschnitten kennengelernt. Dort finden wir einige Varianten der neuen Syntax der switch-Anweisung.

Java 12 mit Gradle: Für Java 12, insbesondere zum Ausprobieren der neuen Syntax bei switch, benötigt man ein aktuelles Gradle 5.1 (oder neuer) und ein paar passende Angaben in der Datei build.gradle (Listing 6).

Listing 6

apply plugin: ’java’
apply plugin: ’eclipse’

sourceCompatibility=12
targetCompatibility=12

repositories
{
  jcenter();
}

// Aktivierung von switch preview
tasks.withType(JavaCompile) {
  options.compilerArgs += ["--enable-preview"]
}

Danach können wir wie gewohnt mit gradle clean assemble ein korrespondierendes JAR bauen und die Klasse SwitchExample wie folgt daraus starten: java –enable-preview -cp build/libs/Java12Examples.jar java12.SwitchExample. In beiden Fällen ist wichtig, den Kommandozeilenparameter –enable-preview anzugeben, denn nur dadurch lässt sich das Programm auch starten.
Java 12 mit Maven: Für Java 12 benötigt man ein aktuelles Maven 3.6.0 und das Maven Compiler Plugin in der Version 3.8.0. Dann kann man die in Listing 7 dargestellten Angaben in der pom.xml vornehmen:

Listing 7

<plugins>
  <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    <configuration>
      <source>12</source>
      <target>12</target>

      <!- - Wichtig für Java 12 Syntax-Neuerungen ->
      <compilerArgs>--enable-preview</compilerArgs>
    </configuration>	
  </plugin>
</plugins>

Mit diesen Modifikationen lässt sich ein Maven-Build mit mvn clean package ausführen. Nun wollen wir die neue switch-Funktionalität in Aktion erleben und geben dazu Folgendes ein:

java --enable-preview -cp target/SimpleJdk12Application-1.0.0-SNAPSHOT.jar
java12.SwitchExample

Dadurch startet die Applikation erwartungskonform.

Java 12 mit Eclipse: Eclipse bietet Stand Ende Februar 2019 noch keinen Support für Java 12. Es ist aber zu vermuten, dass dieser mit dem neuen Märzrelease von Eclipse bereitstehen wird.

Java 12 mit IntelliJ: Um mit Java 12 zu experimentieren, nutzen Sie bitte das aktuelle IntelliJ 2018.3. Allerdings sind noch ein paar Einstellungen vorzunehmen:

 

  • Im Project Structure-Dialog muss man im Bereich Project SDK den Wert 12 auswählen und im Bereich Project language level den Wert 12 – No new language features einstellen.
  • Zusätzlich ist im Preferences-Dialog im Feld Additional command line parameters der Wert –enable-preview anzugeben. Danach kompiliert der Sourcecode, allerdings moniert der Editor derzeit noch verschiedene Dinge fälschlicherweise als „Unexpected Token“. Das kann man aber einfach ignorieren.
  • Zum Ausführen des Beispiels ist es erforderlich, in der Run Configuration auch noch –enable-preview einzustellen.

Stay tuned

Regelmäßig News zur Konferenz und der Java-Community erhalten

 

Fazit

Die Freude über die Neuerungen in Java 12 ist getrübt, weil es die Raw String Literals nicht ins Release geschafft haben. Dafür sind die Verbesserungen im Bereich switch-case sehr angenehm, aber fast schon überfällig. Leider sind auch diese lediglich als Preview integriert und müssen über –enable-preview freigeschaltet werden.

Alle News der Java-Welt:

Behind the Tracks

Agile, People & Culture
Teamwork & Methoden

Clouds & Kubernetes
Alles rund um Cloud

Core Java & Languages
Ausblicke & Best Practices

Data & Machine Learning
Speicherung, Processing & mehr

DevOps & CI/CD
Deployment, Docker & mehr

Microservices
Strukturen & Frameworks

Performance & Security
Sichere Webanwendungen

Serverside Java
Spring, JDK & mehr

Software-Architektur
Best Practices

Web & JavaScript
JS & Webtechnologien

Digital Transformation & Innovation
Technologien & Vorgehensweisen

Domain-driven Design
Grundlagen und Ausblick

Spring Ecosystem
Wissen in Spring-Technologien

Web-APIs
API-Technologie, Design und Management