在Java开关中声明和初始化变量


99

我对Java开关有一个疯狂的问题。

int key = 2;

switch (key) {
    case 1:
        int value = 1;
        break;
    case 2:
        value = 2;
        System.out.println(value);
        break;
    default:
        break;
}

方案1-当key为2时,它成功地将值打印为2。
方案2-当我要在其中注释value = 2case 2:,saying地说“本地变量值可能尚未初始化”

问题:

场景1:如果执行流程没有转到case 1:(当时key = 2),那么它如何知道value变量的类型为int

方案2:如果编译器将value变量的类型识别为int,则它必须已访问。(声明和初始化)中的int value = 1;表达式case 1:。那为什么它会吱吱作响?当我要发表评论value = 2case 2:,说本地变量值可能没有初始化


13
这不是一个疯狂的问题,这是一个很好的问题。
biziclop 2012年


@PhilippeCarriere实际上,我认为应该相反-此处的答案更好(即使该帖子是较新的),因为它直接引用了JLS,并且很好地总结了该帖子中不同答案所涵盖的问题。另请参阅
Tunaki

@Tunaki对于重复项的描述始于“此问题已被问过”。我正在阅读,因为较晚的版本应标记为较早版本的副本。但是我同意这一点有很好的元素。也许应该以某种方式将它们合并?
菲利普·卡里利

同样,关于SO的很多问题都被标记为与原始问题重复,因此,如果您决定最好将此问题标记为新的原始问题,请修复所有链接以引用此问题而不是我的问题。
菲利普·卡里尔

Answers:


114

基本上,switch语句的作用域很奇怪。从JLS的6.3节开始

块中的局部变量声明的范围(第14.4节)是该声明在其中出现的其余部分,从其自身的初始化程序开始,并在局部变量声明语句的右侧包括其他任何声明符。

就您而言,即使永远不会执行,也与它case 2在同一块case 1并在其后出现case 1...所以局部变量在范围内并且可用于编写即使您从逻辑上从未“执行”该声明仍在。(尽管声明是初始化的,但它并不是真正的“可执行文件”。)

如果您注释掉 value = 2;赋值,编译器仍会知道您所指的是哪个变量,但是您将不会经历任何为其分配值的执行路径,这就是为什么您尝试执行此操作时会出现错误的原因读取其他任何未明确分配的局部变量。

我强烈建议您不要使用在其他情况下声明的局部变量-如您所见,这会导致代码非常混乱。当我在switch语句中引入局部变量时(我很少尝试这样做-理想情况下,情况应该很短),我通常更喜欢引入新的作用域:

case 1: {
    int value = 1;
    ...
    break;
}
case 2: {
    int value = 2;
    ...
    break;
}

我相信这很清楚。


11
+1表示“声明实际上不是“可执行的,尽管初始化是可以的”。同时也感谢您的建议Skeet。
namalfernandolk 2012年

1
集成了JEP-325后,局部变量范围图像发生了变化,并且可以在各种情况下使用相同的名称来代替开关块。虽然它也依赖于类似的块编码。同样,对于每个开关情况,分配给变量的值对于开关表达式将非常方便。
Naman

用括号添加新范围的要点。甚至都不知道你能做到。
浮动翻车鱼

21

该变量已声明(作为int值),但尚未初始化(分配了初始值)。想想这行:

int value = 1;

如:

int value;
value = 1;

int value部分在编译时告诉编译器您有一个名为value的变量,它是一个int。该value = 1部件将对其进行初始化,但是会在运行时发生,并且如果未输入该开关的分支,则根本不会发生。


+1可以很好地解释编译时和运行时的声明和初始化。
namalfernandolk 2012年


3

通过JDK-12早期访问版本中的JEP 325:Switch Expressions(预览)的集成。从乔恩的答案中可以看出某些变化 -

  1. 局部变量作用域 -切换案例中的局部变量现在可以是案例本身的局部变量,而不是整个切换块。考虑Day枚举类进行进一步说明的示例(类似于Jon的语法尝试):

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    
    // some another method implementation
    Day day = Day.valueOf(scanner.next());
    switch (day) {
        case MONDAY,TUESDAY -> {
            var temp = "mon-tue";
            System.out.println(temp);
        }
        case WEDNESDAY,THURSDAY -> {
            var temp = Date.from(Instant.now()); // same variable name 'temp'
            System.out.println(temp);
        }
        default ->{
            var temp = 0.04; // different types as well (not mandatory ofcourse)
            System.out.println(temp);
        }
    }
  2. 开关表达式 -如果要给变量赋值然后再使用它,则一次可以使用开关表达式。例如

    private static void useSwitchExpression() {
        int key = 2;
        int value = switch (key) {
            case 1 ->  1;
            case 2 -> 2;
            default -> {break 0;}
        };
        System.out.println("value = " + value); // prints 'value = 2'
    }

0

该说明可能会有所帮助。

    int id=1;

    switch(id){
        default: 
            boolean b= false; // all switch scope going down, because there is no scope tag

        case 1:
            b = false;
        case 2:{
            //String b= "test"; you can't declare scope here. because it's in the scope @top
            b=true; // b is still accessible
        }
        case 3:{
            boolean c= true; // case c scope only
            b=true; // case 3 scope is whole switch
        }
        case 4:{
            boolean c= false; // case 4 scope only
        }
    }

0

Java规范:

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.11

由于带有标签的中断而突然完成的情况由带标签的语句的一般规则处理(第14.7节)。

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.7

带标签的声明:

LabeledStatement:标识符:语句

LabeledStatementNoShortIf:标识符:StatementNoShortIf

与C和C ++不同,Java编程语言没有goto语句。标识符语句标签与出现在带标签语句中任何地方的break(§14.15)或Continue(§14.16)语句一起使用。

带标签的语句的标签范围是紧随其后的语句。

换句话说,情况1,情况2是switch语句中的标签。break和continue语句可以应用于标签。

因为标签共享语句的范围,所以标签内定义的所有变量都共享switch语句的范围。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.