2

As answered in this question here, the scope of a variable inside of a case belongs to the entire switch statement itself, not just the case. Therefore, this does not compile (duplicate local variable):

int key = 2;
switch (key) {
case 1:
    String str = "1";
    return str;
case 2:
    String str = "2";
    return str;
}

I'm interested in mainly two things...

  1. What's the philosophy, or design principle, behind this behavior? (Maybe I'm even asking for the motivation for the switch statement as a whole?)
  2. How does this happen? How does this code look at the bytecode, or even assembly, level?
Community
  • 1
  • 1
Michael
  • 2,673
  • 1
  • 16
  • 27
  • 1
    Look to languages like C for the inspiration. (And there, consider that `switch` statements are philosophically and behaviourally equivalent to computed `goto` statements.) – Oliver Charlesworth Jan 03 '16 at 01:12
  • As for Q.2, that's something you could trivially answer yourself with a few minutes' work... – Oliver Charlesworth Jan 03 '16 at 01:13
  • IIRC, "scope" is pretty strictly defined in terms of `{}` brackets, consistently throughout the entire language. – Louis Wasserman Jan 03 '16 at 01:14
  • Why doesn't every case have its own set of curly braces, though? @LouisWasserman – Michael Jan 03 '16 at 01:16
  • 1
    @Michael Because you can easily do that yourself! It works the same for all other control statements, `if`, `else`, `for`, `while`, etc. all do *not* introduce a new variable scope; you have to do that yourself with a `{` block `}`. Why would they make `switch` any different? – Erwin Bolwidt Jan 03 '16 at 01:24
  • Hmm.. interesting point, but I'd argue the opposite. `if`, `while`, and `for` all do introduce scope. While it's true that you can write it without the brace, that will only work for the next statement. Fundamentally, an `if` statement is a different level of logic where you can now afford to make assumptions about the state of your program; therefore, it deserves its own scope. I feel the same way for a switch statement, but obviously the language designers didn't. @ErwinBolwidt – Michael Jan 03 '16 at 01:28
  • They don't introduce scope. `{` introduces scope. If you write a single statement rather than a block after an `if` statement, it can't be a declaration. – user207421 Jan 03 '16 at 01:36
  • @EJP: I think the difference the OP is hinting at is that the grammar for (say) an `if` is `if (condition) statement` (and equivalent for `while` etc.) Grammar for `switch` doesn't fit this pattern; it's not `case X: statement; case Y: statement; ...`. – Oliver Charlesworth Jan 03 '16 at 01:38
  • @OliverCharlesworth Yeah I think that better clarifies it. I guess, if you break it really down, the question is: do control flow structures warrant their own level of scope? – Michael Jan 03 '16 at 01:42
  • 1
    @Michael: the only way in Java that a new variable scope is introduced is within curly braces. It seems like you're suggesting that a switch case should introduce a new variable scope without curly braces - then it would be the only exception in Java - as to the why, I don't think anyone ever considered making that exception since you can always introduce a new variable scope yourself using a { Block }. – Erwin Bolwidt Jan 03 '16 at 02:02
  • 1
    @OliverCharlesworth I am specifically addressing the OP's claim that '`if, while,` and `for` all do introduce scope'. They don't. For example, the single statement cannot be a declaration-statement. – user207421 Jan 03 '16 at 02:09
  • There is no relationship of source code lexical scopes to [bytecode]. – Holger Jan 04 '16 at 18:41

2 Answers2

5

For better or worse, the semantics of switch in Java were heavily influenced by the semantics of switch in C. And, while we as programmers tend to think of a case label followed by some statements and a break/continue/return as a logical unit, that's not actually how it works, and no such construct exists at the language level. In a switch, break and continue are just statements, and when you execute a switch, you start at the matching case label and execute the remainder of the block. It just so happens that most of the time, you'll hit a break or continue or return before that happens. (See JLS 14.11.) The key sentence is:

All statements after the matching case label in the switch block, if any, are executed in sequence.

Many people believe (IMO, reasonably so) that the switch statement in Java has its priorities backwards; the language treats fallthrough and other control flow oddities as if they were normal case, and break as the exceptional case. But of course, in real code, it's the other way around. (How did Java acquire these backward priorities? By copying from C.)

The scoping rule for switch statements flows pretty directly from this view of the world; if the body of a switch is an undifferentiated block that happens to be peppered with case labels, of course its one big scope. Never mind that this is not actually what almost all developers want almost all the time.

In addition to confusing scoping and fallthrough-by-default, Among the other things people regret about switch in Java is that it is only a statement, not an expression. See JEP 325, which addresses all of these problems (in a backward-compatible way), which will likely be a preview features in Java 12.

Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
-3

Just put a pair of braces around each case clause:

int key = 2;
switch (key) {
  case 1: {
    String str = "1";
    return str;
  } case 2: {
    String str = "2";
    return str;
  }
}

This will compile.

Dima
  • 39,570
  • 6
  • 44
  • 70
  • 6
    I appreciate your time! Maybe I didn't communicate it strongly enough, but I was asking more for the motivation of the behavior of the cases in a switch statement. The linked answer provides the same solution. – Michael Jan 03 '16 at 01:20
  • Ah, I see. Well, I don't think there is a good reason for this (same can be said about many design decisions made in java language, this is by far not the worst problem). – Dima Jan 03 '16 at 01:35