5
votes

Why the Switch Statement in Java could contain a FINAL Variable as the CASE? ##

In JDK7 as checked by me a value cannot be reassigned to final variable, as shown below. But, Why the final variable "x" can be included in a Switch Statement for a case eventhough value for final variable "x" cannot be reassigned?

Why this can be done eventhough Oracle defines that the Java Compiler takes final variable as the value initialized but not the variable name?http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4

Please tell me if this is a Technical Error of the Java Compiler or are there Exceptions or Special Use of checking the Case of a Final Variable in a Switch Statement?

class Example{
    public static void main(String args[]){
        final int x=100;
        //x=200;    //error: cannot assign a value to final variable x

        //The below piece of code compiles
        switch(x){
            case 200: System.out.println("200");
            case 300: System.out.println("300");
        }

    }
}
7
Do you know what switch does? You might want to read the tutorial. - Martin Thoma
Why not be able to switch on compile-time constants? - John Dvorak
The switch isn't reassigning any values. - Jeel Shah
No! @moose please understand me, i know what switch statement does, but, i want to know what is the use of checking different cases of a final variable because it compiles, is this a technical error of the java compiler or are there any exceptions to check a case of a final varible in any Control Statement? - DJ Tishan
Imagine the equivalent in if-else .. does it assign any values ? - user1428716

7 Answers

4
votes

What about this situation?

public class Foo
{
    private final int x;

    public Foo(int x){this.x = x;}

    public void boo()
    {
        switch(x)
        {
            case 200: System.out.println("200");
            case 300: System.out.println("300");
        }
    }
}

or maybe this one:

public static void doSomething(final int x)
{
    switch(x)
    {
        case 200: System.out.println("200");
        case 300: System.out.println("300");
    }
}
3
votes
switch(x){
    case 200: System.out.println("200"); break;
    case 300: System.out.println("300");
}

essentially means

if (x == 200)
  System.out.println("200");
else if (x == 300)
  System.out.println("300");

It's just comparing, not assigning, so the fact that x can't be modified doesn't matter.

Technically you example will be a little different (because you don't have a break):

if (x == 200)
  System.out.println("200");
if (x == 200 || x == 300)
  System.out.println("300");

or something like that.

The fact that x can never be 200 or 300 doesn't make the code not compile. It may however allow Java to optimise the switch statement away.

2
votes

Well, you can pass a final parameter in a function:

//the function doesn't know what x  value is,
//but it knows that it can't modify its value
public someFunction(final int x) {

    x += 1; //compiler error
    switch(x) {
        case 200: System.out.println("200");
            break;
        case 300: System.out.println("300");
            break;
    }
}

//the function doesn't know what x value is,
//but it knows that it can modify it
//for internal usage
public someOtherFunction(int x) {

    switch(x) {
        case 200:
            x += 200;
            break;
        case 300:
            x += 300;
            break;
    }
    System.out.println(x);
}
1
votes

Why would you expect the final modifier to make a difference? Nothing needs to be assigned to the value that's being switched on.

Are you sure you understand what the switch statement does?

1
votes

I think an optimization to remove or warn about switch-statements that are known at compile time to have be evaluated always to the same case is simply not implemented in the compiler, because that is a rare case.

The following code also compiles without warnings or errors.

  switch(3){
    case 2: 
      System.out.println("two"); 
      break;
    case 3: 
      System.out.println("three"); 
      break;
  }

A warning by the compiler about the unreachable code in the case 2: part would be nice, but is not implemented.

0
votes

Most compiler optimize code using optimization algorithm which are rely on heuristics(experience-based techniques) and approximations. The below code would come into control flow analysis. I ran a lot of samples of the program

Case A) if-else with final variable - Compiler throws a warning Dead Code. The generated byte code does not have any if-else statement.

public static void main(java.lang.String[])

Stack=1, Locals=2, Args_size=1
0:  iconst_0
1:  istore_1
2:  return
LineNumberTable: 
line 42: 0
line 54: 2

LocalVariableTable: 
Start  Length  Slot  Name   Signature
 0      3      0    args       [Ljava/lang/String;
 2      1      1    selection       I


 }

Case B) if-else without final variable - No compiler error but no code optimization too.

      final int selection i=100; //case A
      //int selection i=100; //case B

     if(selection==1){
         System.out.println("Hi");
  }else if(selection==2){

  }else{

   }

Case C) if-else with final variable but if-else statement is put in another method say

       computeIfLese(int selection) 
  • No code OPtimization done since this method can be invoked by other instance with different value of parameter ( Obviously).

Since the compiler optimization technique is based on heuristics , there would have been this case as a miss but who would think of the rarest of rarest case.

Comments from the Java Gods awaited... :)

Here is a living proof that Compiler has not optimized this.Check label 5:

public static void main(java.lang.String[]);
Code:
 Stack=2, Locals=2, Args_size=1
0:  bipush  100
2:  istore_1
3:  bipush  100
5:  lookupswitch{ //2
    200: 32;
    300: 40;
    default: 48 }
32: getstatic   #16; //Field java/lang/System.out:Ljava/io/PrintStream;
35: ldc #22; //String 200
37: invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: getstatic   #16; //Field java/lang/System.out:Ljava/io/PrintStream;
43: ldc #30; //String 300
45: invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: return
  LineNumberTable: 
line 11: 0
line 15: 3
line 17: 32
line 18: 40
line 21: 48

LocalVariableTable: 
Start  Length  Slot  Name   Signature
0      49      0    args       [Ljava/lang/String;
3      46      1    selection       I

StackMapTable: number_of_entries = 3
 frame_type = 252 /* append */
 offset_delta = 32
 locals = [ int ]
  frame_type = 7 /* same */
 frame_type = 7 /* same */


 }
0
votes

Beside of having final parameters, a final local variable can hold values which are not known at compile-time:

public static void main(String args[]){
    final int x;

    if (someMethod())
      x = 200;
    else
      x = 300;

    switch(x){
        case 200: System.out.println("200");
        case 300: System.out.println("300");
    }

}