Подтвердить что ты не робот

Почему локальная переменная в цикле расширенного цикла должна быть локальной?

В соответствии с Спецификацией языка Java, § 14.14.2, переменная расширенного цикла for должна быть локальной в цикле. Другими словами, это компилируется:

for (State state : State.values()) {
    // do something for each state
}

но это не так:

State state;
for (state: State.values()) {
    // do something for each state
}

JLS не дает оснований для выбора этого языка. Я могу понять, почему имя типа должно присутствовать, если локальная переменная изменена на final или аннотацией, но я не понимаю, почему запрещенное имя переменной, объявленной в другом месте, недопустимо. Кто-нибудь знает, почему это ограничение было наложено?

ИЗМЕНИТЬ

Несколько ответов до сих пор, похоже, указывают на то, что то, что происходит вне цикла, является основанием для разработки языка таким образом. Возможно, более пристальное изучение того, что говорит JLS, прояснит, почему я не считаю это убедительным. Рассмотрим этот цикл, где State - перечисление:

for (State state : State.values()) {
    // ...
}

State.values() - это массив, поэтому, согласно JLS, цикл функционально идентичен:

State[] a = State.values();
for (int i = 0; i < a.length; i++) {
    State state = a[i];
    // ...
}

Теперь ясно, что этот последний цикл мог быть написан:

State state;
State[] a = State.values();
for (int i = 0; i < a.length; i++) {
    state = a[i];
    // ...
}

Концептуально этот последний (совершенно законный) цикл мог бы использоваться как функциональный эквивалент второго расширенного цикла for выше (тот, который не компилируется).

Аналогично, если stateList является Iterable<State> (а не массивом), этот цикл:

for (State state : stateList) {
    // ...
}

функционально идентичен:

for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    State state = iterator.next();
    // ...
}

Как и раньше, этот последний цикл можно было бы написать:

State state;
for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    state = iterator.next();
    // ...
}

Опять же, это могло быть использовано как функциональный эквивалент (незаконный):

State state;
for (state : stateList) {
    // ...
}

В каждом случае, когда цикл выходит, значение State отлично определено (если, возможно, бесполезно). Кроме того, как и в регулярном цикле, расширенный цикл for с использованием неизвестного неизвестного имени переменной (например, строка State state; отсутствует или выходит за рамки) может быть обнаружена во время компиляции. Итак, с точки зрения дизайна языка, какая проблема? Почему разработчики языка вне закона эту конструкцию?

4b9b3361

Ответ 1

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

int i;
for(i=0;i<10;i++)
  do...something

int j;
for(j=0; i<10; j++)
  do...something

Теперь в приведенном выше коде, если внимательно посмотрите, вы найдете потенциальную ошибку. i был ошибочно использован в цикле, который итерации над j.

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

Ответ 2

Посмотрите, как работает внутренний цикл for-each, см. Как работает цикл Java для каждого цикла?

for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
String item = i.next();
System.out.println(item);
}

Каждый раз, когда он объявляет элемент переменной String. Следовательно, в вашем случае его выполнение существенно

State state;
\\and inside
State state = i.next();

который, очевидно, не работает. Теперь в реализации, они могут скорее сделать только

item = i.next();

но тогда вам всегда нужно определить этот элемент снаружи для каждого, что будет больно большим количеством времени.

Ответ 3

Множество решений на Java больше основано на концепции того, почему "не удастся" удалить x. Почему, допустим, ваш код можно путать, перемещая область вне цикла? Если вам действительно нужен доступ к последнему элементу, есть более чистые способы.

Я полагаю, что кто-то может так или иначе спорить о защите программистов от себя, я смотрю на него больше, поскольку Java защищает меня от людей, которые любят пользоваться такими вещами.

Это не очень хороший язык хобби, это отличный командный язык разработки, где нужно сделать так просто, чтобы следующий парень понял ваш код.

Например, я заметил в комментарии выше, что многие люди выходят из цикла for в определенной точке со значением. Насколько ясно, когда вы просматриваете код, пытаясь найти ошибку? Это люди, использующие аккуратные трюки, подобные этому, от которых я действительно хочу защитить, а не от себя.

Если вы хотите выполнить некоторый код среднего цикла, поместите его в метод и вызовите его из цикла.

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

Если вы всего лишь один программист (не в команде), я бы посоветовал против Java, он не делает аккуратных трюков, и вы не увидите прохладных ежегодных обновлений функций - и довольно медленно программировать (Он очень много времени ловит и исправляет ошибки во времени, затрачиваемом на кодирование, расстраивая людей с его строгими правилами (одно из самых больших преимуществ языка ИМО)

Если вы хотите что-то javaish, попробуйте Scala. Если вы "вынуждены" учиться этому из-за решения в классе, то вы можете попытаться оценить его с точки зрения "команды" и с учетом того, что вы не будете вынуждены использовать его в будущем.

Ответ 4

Возможно, они хотели удалить возможную разницу в семантике между простыми для и расширенными для.

Если бы у вас был, скажем, регулярный цикл:

int i;
for (i=0; i<4; i++)
    ;

то если вы допустили, что цикл выполняется нормально, вы получите это i==4 после цикла, что недопустимо для переменной итерации.

Что тогда, если бы вы могли:

int i;
for (i : myArray)
    ;

Я полагаю, что с точки зрения реализации это было бы проще, если бы в конце i были равны последнему элементу массива. Но должно ли оно вести себя так или должно быть какое-то недопустимое значение, и если да, то что это может быть? Что, если вы выйдете из последней итерации?

Другая возможность заключается в том, что она делает более понятным, что это за цикл и какой тип элемента коллекции. Напротив, простейший вариант - это "наследственная" конструкция в некотором смысле, поэтому они не могли применять подобную логику, не "разбивая" ее или ограничивая ее гибкость.

Я, очевидно, спекулирую, и это может оказаться случайным выбором дизайна.

Ответ 5

Возможно потому, что таким образом он гарантирует, что state будет пустым и не будет иметь начального значения, назначенного ему.