Объявление и инициализация переменных в Java-переключателях

У меня сумасшедший вопрос о 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 - Когда я собираюсь прокомментировать value = 2 в case 2:, он сквозит, говоря, что значение локальной переменной может не быть инициализировано.

Вопросы:

Сценарий 1: Если поток выполнения не переходит в case 1: (когда key = 2), то как он знает тип переменной значения как int?

Сценарий 2. Если компилятор знает тип переменной значения как int, тогда он должен получить доступ к выражению int value = 1; в case 1:. (Декларация и Инициализация). Тогда почему это sqawrk Когда я собираюсь комментировать value = 2 в case 2:, говоря, что значение локальной переменной, возможно, не было инициализировано.

Ответ 1

Операторы switch в основном нечетны в плане охвата. Из раздел 6.3 JLS:

Объем объявления локальной переменной в блоке (§14.4) - это остальная часть блока, в котором появляется объявление, начиная с его собственного инициализатора и включающего любые другие деклараторы справа в заявлении о объявлении локальной переменной.

В вашем случае case 2 находится в том же блоке, что и case 1 и появляется после него, хотя case 1 никогда не будет выполняться... поэтому локальная переменная находится в области видимости и доступна для записи, несмотря на то, что вы логически никогда не выполняете "выполнение" декларации. (Объявление не является "исполняемым", хотя инициализация есть.)

Если вы закомментируете назначение value = 2;, компилятор все еще знает, к какой переменной вы обращаетесь, но вы не пройдете какой-либо путь выполнения, который присваивает ему значение, поэтому вы получаете ошибку как вы бы попытались прочитать любую другую не определенную локальную переменную.

Я бы настоятельно рекомендовал вам не использовать локальные переменные, объявленные в других случаях - это приводит к очень запутанному коду, как вы видели. Когда я ввожу локальные переменные в операторы switch (которые я стараюсь делать редко - случаи должны быть очень короткими, в идеале), я обычно предпочитаю вводить новую область:

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

Я считаю, что это яснее.

Ответ 2

Переменная была объявлена ​​(как int), но не инициализирована (назначено начальное значение). Подумайте о линии:

int value = 1;

Как

int value;
value = 1;

Часть int value сообщает компилятору во время компиляции, что у вас есть переменная с именем value, которая является int. Элемент value = 1 инициализирует его, но это происходит во время выполнения и вообще не происходит, если эта ветвь коммутатора не указана.

Ответ 3

Из http://www.coderanch.com/t/447381/java-programmer-SCJP/certification/variable-initialization-within-case-block

Объявления обрабатываются во время компиляции и не зависят от выполнение потока вашего кода. Поскольку value объявлен в локальном объем блока коммутатора, он может использоваться в любом месте этого блока из пункт его объявления.

Ответ 4

С интеграцией JEP 325: Выражения коммутатора (Preview) в ранние сборки доступа JDK-12. Есть определенные изменения, которые можно увидеть из ответа Джона -

  1. Область локальных переменных - локальные переменные в случаях переключения теперь могут быть локальными для самого случая вместо всего блока переключения. Пример (аналогичный тому, что Джон тоже пытался синтаксически) рассматривает класс перечисления Day для дальнейшего объяснения:

    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'
    }
    

Ответ 5

Это объяснение может помочь.

    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 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
        }
    }

Ответ 6

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.