5

为什么 Java 中的 Switch 语句可以包含一个 FINAL 变量作为 CASE?##

在我检查的JDK7中,无法将值重新分配给最终变量,如下所示。但是,为什么最终变量“x”可以包含在一个案例的 Switch 语句中,尽管最终变量“x”的值不能被重新分配?

为什么即使 Oracle 定义 Java 编译器将 final 变量作为初始化值而不是变量名,也可以这样做?http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4

请告诉我这是否是 Java 编译器的技术错误,或者在 Switch 语句中检查最终变量的大小写是否存在异常或特殊用途?

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");
        }

    }
}
4

7 回答 7

4

这种情况怎么办?

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");
        }
    }
}

或者这个:

public static void doSomething(final int x)
{
    switch(x)
    {
        case 200: System.out.println("200");
        case 300: System.out.println("300");
    }
}
于 2013-02-24T15:37:41.583 回答
3
switch(x){
    case 200: System.out.println("200"); break;
    case 300: System.out.println("300");
}

本质上意味着

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

它只是比较,而不是分配,所以x不能修改的事实并不重要。

从技术上讲,您的示例会有所不同(因为您没有break):

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

或类似的东西。

x永远不会200300不会使代码无法编译的事实。然而,它可能允许 Java 优化 switch 语句。

于 2013-02-24T15:42:11.753 回答
2

好吧,您可以在函数中传递final参数:

//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);
}
于 2013-02-24T15:35:34.300 回答
1

为什么您希望final修饰符有所作为?无需将任何内容分配给正在打开的值。

您确定您了解该switch语句的作用吗?

于 2013-02-24T15:40:14.963 回答
1

我认为删除或警告switch在编译时已知的-statements 总是被评估为相同情况的优化在编译器中根本没有实现,因为这是一种罕见的情况。

以下代码也可以在没有警告或错误的情况下编译。

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

编译器对case 2:部分中无法访问的代码发出警告会很好,但没有实现。

于 2013-02-24T16:08:25.953 回答
0

除了具有最终参数之外,最终局部变量还可以保存在编译时未知的值:

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");
    }

}
于 2013-04-03T20:32:53.257 回答
0

大多数编译器使用优化算法优化代码,这些优化算法依赖于启发式(基于经验的技术)和近似值。下面的代码将进入控制流分析。我运行了很多程序示例

案例 A) if-else with final 变量 - 编译器抛出警告死代码。生成的字节码没有任何 if-else 语句。

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


 }

案例 B)没有最终变量的 if-else - 没有编译器错误,但也没有代码优化。

      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{

   }

案例 C) if-else 带有最终变量,但 if-else 语句放在另一种方法中说

       computeIfLese(int selection) 
  • 没有进行代码优化,因为该方法可以被具有不同参数值的其他实例调用(显然)。

由于编译器优化技术是基于启发式的,所以会出现这种情况,但谁会想到最罕见的情况。

等待 Java 众神的评论... :)

这是 Compiler 没有对此进行优化的活生生的证明。检查标签 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 */


 }
于 2013-02-25T23:52:32.477 回答