Почему объявление переменной требуется внутри цикла 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 } } 

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

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

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

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

Редактировать Вот интересная статья о циклах foreach в Java.

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

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

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

  0 aload_0 1 invokevirtual #2  4 astore_1 5 aload_1 6 invokeinterface #3  count 1 11 ifeq 34 (+23) 14 aload_1 15 invokeinterface #4  count 1 20 checkcast #5  23 astore_2 24 getstatic #6  27 aload_2 28 invokevirtual #7  31 goto 5 (-26) 34 return 

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

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

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

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

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

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

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

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

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

  • Почему шестнадцатеричные числа с префиксом 0x?
  • Xcode: код теряет синтаксическую окраску
  • Ошибка 1064 в CREATE TABLE ... TYPE = MYISAM
  • Как работает цикл for, особенно для (;;)?
  • Ошибка с varargs для объектов-функций в Scala?
  • Каковы правила, регулирующие использование скобок в вызовах функций VBA?
  • Как я могу разделить команду оболочки на несколько строк при использовании оператора IF?
  • $ (document) .on ('click', '#id', function () {}) vs $ ('# id'). on ('click', function () {})
  • Почему операторы присваивания возвращают значение?
  • Как конкатенировать строки в веточке
  • Инициализация массива C ++
  • Interesting Posts

    Переопределить атрибут авторизации в ASP.NET MVC

    Выражение LINQ для возврата значения свойства?

    Как игнорировать орфографию и грамматику для определенных стилей текста в Word 2007

    Как показать Snackbar в верхней части экрана

    Windows XP не удается снять атрибут «Только для чтения» из папки (и подпапок)

    Есть ли способ «взломать» окна 10, чтобы использовать всю мощность процессора в одной задаче или процессе?

    Где я могу получить streamобезопасный CollectionView?

    Как я могу предотвратить изменение пользователем разрешения (или пользователя) приложения?

    Найти расстояние между двумя точками, используя широту и долготу в mysql

    Двоичная бомба – Фаза 4

    Использовать собственный файл манифеста и разрешение в Unity?

    Ошибка в : цель присвоения расширяется до неязыкового объекта

    Использование Build Flavors – структурирование исходных папок и build.gradle правильно

    SimpleDateFormat игнорирует месяц при parsingе

    Вызов статического метода с использованием отражения

    Давайте будем гением компьютера.