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

Запитання:

Сценарій 1: Якщо потік виконання не надходить до case 1:(коли key = 2), то звідки він знає тип змінної значення int?

Сценарій 2: Якщо компілятор знає тип змінної значення int, тоді він повинен мати доступ до int value = 1;виразу в case 1:. (Декларація та ініціалізація). Тоді чому це sqawrk Коли я буду коментувати value = 2в case 2:, кажучи , що Значення локальної змінної не може бути инициализирован .


13
Це не божевільне питання, це дуже гарне питання.
biziclop


@PhilippeCarriere Насправді, я думаю, що це повинно бути зворотно - відповідь тут краща (навіть якщо публікація новіша), оскільки є пряме посилання на JLS, і добре узагальнює проблему, висвітлену в різних відповідях у цій публікації. Див. Також .
Tunaki

@Tunaki Опис дубліката починається з "Це питання вже задавали раніше". Я читаю, що пізніший слід позначити як дублікат попереднього. Але я погоджуюсь, що цей має приємні елементи. Може, їх слід якось об’єднати?
Філіп Каррієр

Також багато питань щодо SO позначено як дублікати мого оригінального запитання, тому, якщо ви вирішите, що краще позначити це як новий оригінал, виправте всі посилання, щоб посилатися на це замість мого.
Філіп Каррієр

Відповіді:


114

Заяви про перемикання, в основному, дивні з точки зору масштабу. З розділу 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;
}

Я вважаю, що це зрозуміліше.


11
+1 для "Декларація насправді не є" виконуваною ", хоча ініціалізація є". І дякую за поради також Скіт.
намальфернандолк

1
Завдяки інтегрованому JEP-325 зображення області дії локальних змінних змінилося, і можна використовувати одне і те ж ім'я в різних випадках замість блоків перемикачів. Хоча він також покладається на подібне блокове кодування. Крім того, значення, присвоєне змінній для кожного випадку комутатора, було б набагато зручнішим для виразів комутатора.
Naman

Точки для додавання нового обсягу за допомогою фігурних дужок. Навіть не знав, що ти можеш це зробити.
Плавуча сонячна

21

Змінна оголошена (як int), але не ініціалізована (присвоєно початкове значення). Подумайте про рядок:

int value = 1;

Як:

int value;
value = 1;

Ця int valueчастина повідомляє компілятору під час компіляції, що у вас є змінна, яка називається value, яка є int. Thevalue = 1Частина инициализирует його, але це відбувається під час виконання, і не буває зовсім , якщо ця гілка перемикача не введена.


+1 за приємне пояснення декларування та ініціалізації під час компіляції та виконання.
namalfernandolk

18

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

Декларації обробляються під час компіляції і не залежать від потоку виконання вашого коду. Оскільки valueвін оголошений в локальній області блоку перемикача, він може використовуватися в будь-якому місці цього блоку з точки його оголошення.


1
чому ця відповідь голосує? воно не відповідає на запитання, на відміну від відповіді Пола чи
Скіта

7
Це робить. Отже, +1, копійка, і з мого боку.
Ravinder Reddy,

3

Завдяки інтеграції JEP 325: переключення виразів (попередній перегляд) у збірках раннього доступу JDK-12. Є певні зміни, які можна побачити з відповіді Джона -

  1. Локальна змінна сфера - Локальні змінні у випадках комутатора тепер можуть бути локальними для самого випадку, а не для всього блоку комутаторів . Приклад (подібний до того, що Джон намагався також виконати синтаксично), що розглядаєDayклас enum для подальшого пояснення:

    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: Ідентифікатор: Statement

LabeledStatementNoShortIf: Ідентифікатор: StatementNoShortIf

На відміну від C та C ++, мова програмування Java не має оператора goto; мітки виписок ідентифікатора використовуються з операторами break (§14.15) або продовження (§14.16), які з’являються де завгодно в межах міченого оператора.

Сфера дії мітки маркованого висловлення - це одразу міститься Заява.

Іншими словами, випадок 1, випадок 2 є мітками в операторі switch. оператори break і continue можна застосовувати до міток.

Оскільки мітки мають спільний обсяг оператора, усі змінні, визначені в мітках, мають спільний обсяг оператора switch.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.