How Switch Expressions Are Explained

The four-form switch syntax is now final in Java 14. Here I look at some evidence suggesting that the feature may be harder to teach than its creators anticipated.

The Four Forms of Switch

.jpg

As of Java 14, there are four forms of switch constructs. You can have switch expressions (with a value) and switch statements (without a value). With each, you can have disjoint branches or fallthrough. In the latter case, execution falls through to the next branch unless a branch was explicitly terminated.

The following table, whose design I first saw in a blog by Dustin Marx, neatly summarizes these four forms.

Expression Statement
Disjoint branches
int numLetters = switch (day) {
   case MONDAY, FRIDAY, SUNDAY -> 6;
   case TUESDAY -> {
      logger.info("Tuesday");
      yield 7;
   }
   case THURSDAY, SATURDAY -> 8;
   default -> 9;
};
switch (day) {
   case MONDAY, FRIDAY, SUNDAY ->
      numLetters = 6;
   case TUESDAY -> {
      logger.info("Tuesday");
      numLetters = 7;
   }
   case THURSDAY, SATURDAY ->
      numLetters = 8;
   default ->
      numLetters = 9;
}
Fallthrough
int numLetters = switch(day) {
   case MONDAY, FRIDAY, SUNDAY:
      yield 6;
   case TUESDAY:
      logger.info("Tuesday");
      yield 7;
   case THURSDAY:
      logger.info("Thursday");
   case SATURDAY:
      yield 8;
   default:
      yield 9;
};
switch(day) {
   case MONDAY, FRIDAY, SUNDAY:
      numLetters = 6;
      break;
   case TUESDAY:
      logger.info("Tuesday");
      numLetters = 7;
      break;
   case THURSDAY:
      logger.info("Thursday");      
   case SATURDAY:
      numLetters = 8;
      break;
   default:
      numLetters = 9;
}

How many programmers really want a multi-way branch expression with fallthrough? Maybe nobody, but the language designers apparently felt that offering all combinations makes the feature more regular and more teachable.

Evidence

The switch syntax was a preview feature of JDK 12 and (with one small change in syntax) in JDK 13. It is final in JDK 14.

To my knowledge, the twelve month preview period yielded no significant evidence supporting the design. And that is not surprising. Clearly people had better things to do than to rewrite existing switch statements. And not much new code is written with preview features.

Of course, that is only to be expected. So, where might evidence come from?

The design of records (JEP 359) was supported by data. As it happened, Google had a similar feature in an internal code generation tool. Their design decisions closely mirrored those of Java records. Programmers made some use of the feature. This provided external validation for the Java design.

The Java switch design is, however, quite different from every other multi-way branch out there. Kotlin, Scala, and C# don't offer switch expressions with fallthrough.

But there is a way to gauge the teachability of the new design, namely to see how people teach it. Consider Rémi Forax' Java guide that presents modern Java without the historical baggage. For example, records and interfaces are presented before classes, which makes a lot of sense.

How does Rémy explain switch? He starts with the switch statement with disjoint branches, then the switch expression with disjoint branches, and then the classic switch statement with fallthrough. Not a peep about expressions with fallthrough. Hmmm...that's not the “let 2x2 combinations bloom” explanation.

So, I decided to inspect other attempts to teach this feature. The blogosphere has no shortage of explanatory material, and I looked at ten of them:

  1. The JEP itself
  2. Nikolai Parlog
  3. Mohamed Taman
  4. Mykong.com
  5. Urma and Warburton, Java Magazine
  6. Gurpreet Sachdeva
  7. Dustin Marx
  8. Logicbig.com
  9. Urma and Warburton again
  10. Daniel Warren

As you can see, a good number of authors, some very well-known, have endeavoured to explain the new switch features. One of them (Dustin Marx) uses the 2x2 matrix. The JEP describes the two axes—expression vs. statement and disjoint branches vs. fallthrough. The others, not so much. The forms are presented one after another, in various orders and no systematic effort to relate them to the two axes.

How to Explain switch

If I had to explain switch to an experienced Java programmer, I would go with the two axes.

First switch expressions vs. statements. Experienced programmers understand what expressions and statements are, so this should not be too difficult: The switch statement relates to the switch expression just like if/else relates to the ? : operator. Think of yield as a mixture between return and break.

Next, disjoint branches vs. fallthrough. Experienced programmers know the classic switch statement. They will have been warned against the dangers of fallthrough. They will know that they have rarely, if ever, used fallthrough. They will find it easy to distinguish the new -> for disjoint branches from the familiar : for fallthrough.

For beginning programmers who have never seen switch before, however, it is no walk in the park. Beginners are not very clear about the difference between expressions and statements. The Java switch statements and expressions look exactly alike. (In C#, switch expressions look like day switch { case ... }.)

I would start with the first quadrant, which I expect in time to be the most common case. The yield keyword would be bothersome since switch is typically introduced before functions, and I can't yet use an analogy with return.

Then I would introduce the modern statement form. Should I mention break? Students haven't seen loops yet, so this would be their first exposure to that sticky wicket.

And then? Should I dismiss fallthrough as a historical artifact, almost never used?

Conclusion

I am not saying that these blogs are conclusive evidence that the four-form design has failed its teachability goal. But they certainly aren't a rousing endorsement either.

Stepping back, switch expressions were never meant to be an end to themselves but a first step for pattern matching, which is now being designed. Consider this email about nullability in patterns that comments on switch's legacy hostility to null. That legacy would not matter if pattern matching was based on some construct designed to fit pattern matching, rather than the legacy switch.

It is a moot point now, since the switch syntax has been finalized. To me, this points to a weakness of the preview mechanism in its current form. It is too short for significant evidence to emerge.

Comments powered by Talkyard.