1

I'm preparing for Java7 Oracle certificate and I'm wondering why Java handles switch-case in such a weird way:

    public static void main(String[] args) {
    int a = 2;
    switch (a) {
        case 0:
            //System.out.println(b); // cannot find symbol variable b
            break;
        case 1:
            boolean b=false;
            break;
        case 2:
            b = true; // how case knows about existence of variable b?
            break;
        case 3:
            //System.out.println(b); // var b might not have been initialized
            break;
    }
}

That's obvious why case 0 and case 3 inform about unknown symbol or uninitialized variable, but I can't imagine why case 2 is working properly? How compiler knows about the type of b if it's assigned in case 1? For me that's not coherent.

UPDATE1: Actually what is even more weird is that in case 3 I noticed compile time error (Linux64, Java7 and Java8), but some of my colleagues do not.

I prepared very simple maven project: https://github.com/gonciarz/switch-test

UPDATE2: It seems that for everyone case3 does not compile. Thank you guys for your clarifications.

Robert Gonciarz
  • 363
  • 2
  • 8
  • 15
  • it is in it's scope! cases is not like `if` condition. – Lrrr May 26 '15 at 10:41
  • is there a class member b ? – Stultuske May 26 '15 at 10:41
  • 1
    Do you really get that error in case 3? You shouldn't get that. I don't. – K Erlandsson May 26 '15 at 10:42
  • possible duplicate of [Java switch : variable declaration and scope](http://stackoverflow.com/questions/10932997/java-switch-variable-declaration-and-scope) – GhostCat May 26 '15 at 10:46
  • @Jägermeister: That's similar case but not a duplicate. In the mentioned question there was a "duplicate local variable" and in mine there is "variable b might not have been initialized" – Robert Gonciarz May 26 '15 at 11:50
  • @K Erlandsson could you please put your platform and java version. I'm using Linux 64 bit + JDK 1.7.0.13 64 bit. I'll try to create a minimalistic Maven project just to make sure we see different behavior. Maybe that's a JVM defect. – Robert Gonciarz May 26 '15 at 11:53
  • @Stultuske, no, in the class there was only main method. – Robert Gonciarz May 26 '15 at 11:54
  • 1
    As for you getting error messages but not your colleagues, are you aware that IDE's such as Eclipse make it highly configurable which scenarios are flagged an error and which not ? Many of these messages originate in the IDE, not the language compiler. – Erwin Smout May 26 '15 at 13:44
  • 2
    If your colleagues do not notice the compile time error, that might be a sensory problem of your colleague… – Holger May 26 '15 at 14:00
  • @ErwinSmout: thanks for advice, I recreated that on both InteliJ and maven command line – Robert Gonciarz May 26 '15 at 18:22
  • K Erlandsson rectified that it was IDE problem (that's why the code in case3 was compiling) – Robert Gonciarz May 26 '15 at 18:23

3 Answers3

4

You are mixing up declaration and assignment. In your code

case 1:
    boolean b=false;
    break;
case 2:
    b = true; // how case knows about existence of variable b?
    break;

the variable b is declared and assigned in case 1:. The scope of the declaration is the rest of the switch statement’s block, i.e. up to the }.

It’s the same as if you had written:

case 1:
    boolean b;// declaration
    b=false;  // assignment
    break;

The assignment has no effect for the code flow for the case 2: label. If a jump to the case 2 label happens, the variable is declared but not assigned as an assignment can be skipped but not a declaration.

So the behavior is consistent with your third label:

case 3:
    System.out.println(b); // var b might not have been initialized
    break;

Here, the variable b is declared, but not assigned. That’s what the error message tells you: it doesn’t say that b was undeclared but that it “might not have been initialized”, which includes the possibility that you know for sure, that it has not been initialized at this point.

The type of b is the declared type and not dependent on any assignment.


As a side note, there are differences between switch and if. Had you written

final int a = 2;
boolean b;
if(a == 2) b=false;
System.out.println(b);

the constant nature of a caused the compiler to detect that b will be assigned to false and no error was generated. That works for if but not for switch.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • But if the declaration works, the compiler should know that it is a `boolean` which is initialized to `false`. (e.g. `boolean b; System.out.println(b);` compiles). Why does it now work in this case? If it was a `Boolean` this would totally make sense but as it is now it confuses me. – K Erlandsson May 26 '15 at 17:03
  • 1
    @K Erlandsson: No, *again*, the *declaration* is not the *assignment*. The declaration says that there is a variable `b` of type `boolean`. In the question’s code, the assignment `b=false` is only executed for the `case 1:` label, in other words if `a` has the value `1`, *not* if `a` has the value `3`. And `boolean b;System.out.println(b);` does *not* compile. – Holger May 26 '15 at 17:30
  • I was sloppy in my test, it does indeed not compile, sorry. Eclipse messes with me and hides the error messages for these things some times. It seems I am confusing field declarations with local variables; a boolean field is initalized to `false` while a local variable is not. Thank you for your response. – K Erlandsson May 26 '15 at 17:39
  • @K Erlandsson: right, fields are initialized to default values (except for `final` fields) but then the question does not arise as the field will be defined throughout the entire `switch` statement. – Holger May 26 '15 at 17:41
1

Your scope here is the entire switch clause, not the separate cases. Scopes are defined by the { and }.

When you declare your variable with boolean b = false it is accessible in the rest of the scope, i.e. the rest of the switch clause.

You do not get an error in case 3 as you mention in your question.

Edit: You do get an error in case 3 but it seems Eclipse is sloppy in detecting it. I was confusing field declarations which get a default value for primitives with local variables which do not.

K Erlandsson
  • 13,408
  • 6
  • 51
  • 67
  • Actually I got the error: Information:Using javac 1.7.0_13 to compile java sources Information:java: Errors occurred while compiling module 'cert-test' Information:5/26/15 1:42 PM - Compilation completed with 1 error and 0 warnings in 1s 72ms /home/XXX/workspace/java/cert-test/src/main/java/cases2/others/Sw.java Error:(18, 36) java: variable b might not have been initialized – Robert Gonciarz May 26 '15 at 11:43
1

The rules says this:

The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.

and this

Declarations are processed at compile time and do not depend on the execution flow of your code. Since value is declared within the local scope of the switch block, it is useable anywhere in that block from the point of its declaration.

João Marcos
  • 3,872
  • 1
  • 19
  • 14
  • Referring to the second part. If the declarations and processed at compile time do not depend on the execution flow, then I should not get the error that variable b might not have been initialized – Robert Gonciarz May 26 '15 at 11:48
  • Of course you should get that message. In that case, the declaration has been done, but nowhere in the code execution path does it get assigned a value. Hence uninitialized. – Erwin Smout May 26 '15 at 13:41