In Java 13 there’s an experimental feature called switch expressions introduced by JEP-354. It basically allows you to write a switch block as if it were an expression:

void expr() {
    int i = (int)(Math.random() * 2);
    String s = switch (i) {
        case 1 -> "one";
        case 2 -> "two";
        default -> "other";
    };
    System.out.println(s);
}

See how the result of the switch is directly assigned, shortening the block and eliminating the requirement of break?

One might say it is just (very nice) syntactic sugar, similar to Kotlin’s when expression. But, is it? Let’s find out what byte code the compiler produces for a plain old Java switch block v.s. the new switch expression.

Let’s take the following 2 methods:

Java13SwitchExpressions.java
public class Java13SwitchExpressions {

    public void plainOldJava() {
        int i = (int)(Math.random() * 2);
        String s;
        switch(i) {
            case 1:
                s = "one";
                break;
            case 2:
                s = "two";
                break;
            default:
                s = "other";
        }
        System.out.println(s);
    }

    public void switchExpression() {
        int i = (int)(Math.random() * 2);
        String s = switch (i) {
            case 1 -> "one";
            case 2 -> "two";
            default -> "other";
        };
        System.out.println(s);
    }
}

The one using Java 13’s switch expression is clearly shorter to write and easier to read, but what does the byte code look like?

Compiling and Disassembling

Since switch expressions are an experimental feature in Java 13 you have to provide the –enable-preview flag to the compiler. After compilation, we can use JDK’s disassembler (javap) to find out what the byte code looks like:

compile-disassemble.sh
javac Java13SwitchExpressions.java --enable-preview --release 13
javap -v Java13SwitchExpressions.class

For the plain old Java switch block this gives the following output:

plain-old-switch-block.txt
 0: invokestatic  #7                  // Method java/lang/Math.random:()D
 3: ldc2_w        #13                 // double 2.0d
 6: dmul
 7: d2i
 8: istore_1
 9: iload_1
10: lookupswitch  { // 2
               1: 36
               2: 42
         default: 48
    }
36: ldc           #15                 // String one
38: astore_2
39: goto          51
42: ldc           #17                 // String two
44: astore_2
45: goto          51
48: ldc           #19                 // String other
50: astore_2
51: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
54: aload_2
55: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

The Java 13 switch expression looks the following:

java13-switch-expression.txt
 0: invokestatic  #7                  // Method java/lang/Math.random:()D
 3: ldc2_w        #13                 // double 2.0d
 6: dmul
 7: d2i
 8: istore_1
 9: iload_1
10: lookupswitch  { // 2
               1: 36
               2: 41
         default: 46
    }
36: ldc           #15                 // String one
38: goto          48
41: ldc           #17                 // String two
43: goto          48
46: ldc           #19                 // String other
48: astore_2
49: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
52: aload_2
53: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
56: return

The generated byte code looks very much alike but the switch branch handling differs. The handling of a switch block branch works like this:

Byte code Explanation
ldc Push constant (our string) to the stack
astore_2 Store reference in local variable 2
goto 51 Jump to ‘line’ 51
(line 51..n) Invoke System.out.println

Handling of our switch expressions branches looks like this:

Byte code Explanation
ldc Push constant (our string) to the stack
goto 48 Jump to ‘line’ 48
(line 48) astore_2 Store reference in local variable 2
(line 49..n) Invoke System.out.println

The difference is that in a switch block, the value is stored (astore_2) in each branch, whereas when using a switch expression the resulting value is stored ‘at the end’ after jumping out of the switch. That’s exactly what you’d expect when reading the Java source, which surprised me since generated byte code often differs quite a bit from its Java source.

Conclusion

Although the differences are minor the byte code is different, so Java 13’s switch expressions are more than just syntactic sugar.

Either way, I’m very happy that we can now write Kotlin-esque switch expressions.