Confessions of a Fallthrough-Hater

Java is getting a multi-way selection expression, and an updated switch statement. Join in for more bikeshedding .

trafficlight

In my previous blog, I expressed my frustration about the design of a multi-way selection expression, AKA “expression switch”.

My personal feeling is that the existing switch statement is toxic and should be left in a dark corner of the bike shed. What's bad about it? In a word: fallthrough. It is well-established that fallthrough is rarely useful, but confusing for many programmers (even though it may be crystal clear to you). In the parlance of the amber-spec-experts mailing list, this makes me a “fallthrough-hater”, a designation that I accept with pride.

Now the thinking of the spec group has evolved. They have embraced a switch statement without fallthrough. Dustin Marx has a nice table on his blog that I am adapting here, with color coding added for your convenience:

Expression Statement
No fallthrough
int numLetters = switch (day) {
   case MONDAY, FRIDAY, SUNDAY -> 6;
   case TUESDAY -> 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:
      break 6;
   case TUESDAY:
      logger.info("Tuesday");
      break 7;
   case THURSDAY:
      logger.info("Thursday");
   case SATURDAY:
      break 8;
   default:
      break 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;
}

Check it out. The top left corner is the expression switch with arrows, as originally proposed in JEP 325. Nice and intuitive. The top right corner is the statement equivalent. After the arrow, you can put in a single statement, not a statement sequence. This can be a block statement enclosed in braces. I am all for it.

The bottom right is the same old switch we've always had, except you can now write

case MONDAY, FRIDAY, SUNDAY:

instead of

case MONDAY: case FRIDAY: case SUNDAY:

Whatever.

Now on to the gruesome part—the bottom left. Here we have a switch expression. The break ... statements act like miniature return statements, yielding the expression values.

Why have it at all? Java has no block expression, so there is no way of adding a logging call in an arrow switch expression. They language designers would be laughed out of the room if they couldn't solve that. Just like the designers of the ? : operator were, when they slunk away in shame way back when.

Just kidding about the ? : designers of course. In C, there is a block expression, called the comma expression:

int sign = x < 0 ? (printf("negative\n"), -1) : 1; // C

And in Java, you can write a helper method:

public static <T> T call(Callable<T> c) {
   try { return c.call(); }
   catch (Throwable e) { throw new RuntimeException(e); }
}

Then you can carry out any desired actions before yielding a value:

int sign = x < 0 ? call(() -> { logger.info("negative"); return -1; }) : 1;

Not so lovely, I admit.

Note, by the way, that, unlike prior proposals, this proposal outlaws mixing case : and case -> in the same switch. Hooray for that. There is just one teensy thing. Suppose you wrote a long arrow expression switch and then you note that you need to add a logging call into one branch. Now you have to rewrite the entire thing with the case : syntax. Presumably the IDE could do that transformation for you. Or you could do the unlovely helper method trick.

I am not sure if it is still proposed that one can use block statements with value-returning break statements, like:

case TUESDAY -> { logger.info("Tuesday"); break 7; } 

As I said in my prior blog, that would be rather weird when compared with lambda expressions:

Callable<Integer> task = () -> { logger.info(); return 7; };

Perhaps it would be better if we had a genuine block expression in Java:

case TUESDAY -> { logger.info("Tuesday"); 7 };

and

Callable<Integer> task = () -> { logger.info(); 7; };

But if it's too late for that, why not allow a comma-separated expression list, just like in the third slot of the for statement:

case TUESDAY -> logger.info("Tuesday"), 7;

I know, it's something new (but with precedent)—as is break 7. But it comes without the fallthrough baggage.

Comments powered by Talkyard.