Почему объявление переменной требуется внутри цикла for-each в java - программирование
Подтвердить что ты не робот

Почему объявление переменной требуется внутри цикла for-each в java

Обычно для каждого цикла используется обычная форма:

for(Foo bar: bars){
    bar.doThings();
}

Но если я хочу сохранить бар до цикла, я не могу использовать для каждого цикла:

Foo bar = null;
// - Syntax error on token "bar", Identifier expected after this token
for(bar: bars){ 
    if(bar.condition())
        break;
}
bar.doThings();

В цикле for получена синтаксическая ошибка, упомянутая выше.

Почему это? Меня не интересуют обходные пути, но просто любопытно соображения, лежащие в основе этого ограничения.

В отличие от обычного цикла, переменная может быть объявлена ​​вне или вообще не...

int i = 1;
for(;i<max;i++){
    for(;;){
        // Do things
    }
}
4b9b3361

Ответ 1

Это хороший вопрос, и я был бы рад увидеть какой-то подробный ответ. Однако официальная документация гласит:

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

Подавляющее большинство случаев - это ответ для меня.

На стороне примечания, лично, я думаю, что цикл foreach в Java - это просто хороший синтаксис для стандартного цикла итератора. Таким образом, компилятор создает итератор для структуры и использует переменную для получения значения для текущей итерации. Чтобы убедиться, что переменная была инициализирована, вам нужно объявить ее для области цикла (и я думаю, что это предотвращает использование переменной в другом месте, например, в другом потоке). Из-за этого вы не можете использовать переменную после цикла. Но, это только мое мнение, и я был бы очень рад услышать от кого-то, кто знает это лучше.:)

Изменить Вот интересная статья о циклах foreach в Java.

Другое редактирование Я проанализировал (с jclasslib байт-код этих методов:

 private static void testForEach(ArrayList<String> als) {
  for(String s: als)
    System.out.println(s);
 }

 private static void testIterator(ArrayList<String> als) {
  for(Iterator<String> is = als.iterator(); is.hasNext();) {
   String s = is.next();
   System.out.println(s);
  } 
 }

Оба метода представлены одним и тем же байткодом:

 0 aload_0
 1 invokevirtual #2 <java/util/ArrayList.iterator>
 4 astore_1
 5 aload_1
 6 invokeinterface #3 <java/util/Iterator.hasNext> count 1
11 ifeq 34 (+23)
14 aload_1
15 invokeinterface #4 <java/util/Iterator.next> count 1
20 checkcast #5 <java/lang/String>
23 astore_2
24 getstatic #6 <java/lang/System.out>
27 aload_2
28 invokevirtual #7 <java/io/PrintStream.println>
31 goto 5 (-26)
34 return

Разница заключается в строке 1, последний метод использует invokevirtual #8. Однако оба вызова приводят к вызову того же метода Iterator. Таким образом, кажется, что foreach - не что иное, как синтаксический сахар, который компилятор просто переводит в предопределенную конструкцию, точно так же, как говорится в документации. Это не отвечает на вопрос, почему так оно и есть. Я, хотя это просто интересно и, возможно, стоит знать, в контексте обсуждения.

Ответ 2

Я предполагаю, что они просто хотели продвинуть "хорошие практики": из языкового руководства Java :

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

Как мы знаем, поведение, о котором вы просите, можно моделировать, просто расширив цикл for-each до старого стиля, основанного на итераторе, и, возможно, они просто подумали, что это достаточно хорошо.

Ответ 3

В этом случае способ перебора по списку:

Foo bar = null;
for(Iterator<Foo> barIterator = bars.iterator(); barIterator.hasNext();){ 
   bar = barIterator.next(); // or whatever else you want to do
   ...
}

Не перебирайте список с помощью индекса, если только этот список не реализует RandomAccess!

Расширенный цикл for - это просто синтаксический сахар для этого подхода итератора, который сохраняет переменную (здесь bar) локально в цикле. (Как правило, сохранение переменных как можно более локальным - это хорошо.)

Ответ 4

Цель этого нового синтаксиса заключается в благоустройстве, поскольку на самом деле он не добавляет ничего нового в Java. Я предполагаю, что они не позволяли синтаксис, который вы делали по единственной причине: Это не красиво!:)