Объявление и инициализация переменных в 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. Есть определенные изменения, которые можно увидеть из ответа Джона -
-
Область локальных переменных - локальные переменные в случаях переключения теперь могут быть локальными для самого случая вместо всего блока переключения. Пример (аналогичный тому, что Джон тоже пытался синтаксически) рассматривает класс перечисления 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);
}
}
-
Выражения-переключатели - если цель состоит в том, чтобы присвоить значение переменной и затем использовать его, один раз можно использовать выражения-переключатели. например
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.