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

Улучшено для компиляции в петле для JDK 8, но не 7

Рассмотрим следующий фрагмент кода, который я напутал после некоторого рефакторинга, когда проверил, почему сервер сборки сообщил о неисправной сборке, но в моей среде IDE было хорошо:

List<String> text;
...
for (String text : text) {...}

Итак, одно и то же имя используется для String и List внутри for-each.

Это, конечно, не очень мудро, но после того, как я перешел на мою ношу, чтобы переименовать его, я увидел, что выше код компилируется с JDK 8, но дает следующую ошибку с JDK 7:

  error: for-each not applicable to expression type
        for (String text : text) {
                           ^
  required: array or java.lang.Iterable
  found:    String
1 error

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


Update: Поскольку у меня есть некоторые комментарии о поведении, здесь полный образец класса:

import java.util.Arrays;
import java.util.List;

public class Strange {

    List<String> text = Arrays.asList("Max", "Alex", "Maria");

    public static void main(String[] args) {
        new Strange().doSomething("Alex");
    }

    public void doSomething(String name) {
        for (String text : text) {
            System.out.println(text.equals("Alex"));
        }
    }

}

И вот процесс компиляции и вывод (Windows 7 64bit):

C:\copy>c:\Projects\java\jdk1.7.0_79\bin\javac.exe Strange.java
Strange.java:13: error: for-each not applicable to expression type
        for (String text : text) {
                           ^
  required: array or java.lang.Iterable
  found:    String
1 error

C:\copy>c:\Projects\java\jdk1.8.0_60\bin\javac.exe Strange.java

C:\copy>

Вывод: Я был так озадачен, почему моя IDE (которая использует 8) не жаловалась примерно на одно и то же имя в одном утверждении, но теперь ясно, что это не один оператор. Мне действительно интересно, почему этот момент так долго существовал, если JLS заявляет об обратном. Но в любом случае, спасибо за полученные мной идеи и отличные ответы (что затрудняло мне выбор лучшего).

4b9b3361

Ответ 1

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

§6.3. Сфера применения Декларации

...

Область локальной переменной, объявленной в части FormalParameter расширенный оператор for (§14.14.2) это содержащееся выражение.

(прямая ссылка)

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

Вы можете проверить, что это не изменилось, по сравнению с Java 7 и Java 6, хотя оба (я пробовал Java 6 javac) демонстрируют противоречивое поведение.

Итак, это изменение в поведении компилятора - это исправление старой ошибки...

Ответ 2

Это должно скомпилировать штраф для JDK 7 и 8.

Цитата JLS раздел 14.14.2 (что то же самое для спецификации Java 7):

Усиленный оператор for эквивалентен основному выражению формы:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
      {VariableModifier} TargetType Identifier =
          (TargetType) #i.next();
      Statement
}

Переписывание усиленного цикла цикла с помощью Iterator

for (String text : text) {...}

становится

for (Iterator<String> it = text.iterator(); it.hasNext(); ) {
    String text = it.next();
}

Затем, цитируя пример 6.4.1 JLS:

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

Таким образом, здесь нет ошибки времени компиляции, потому что при теневой переменной-члене локальной переменной не существует ограничений, которая здесь имеет место: локальная переменная String text затеняет переменную-член List<String> text.

Ответ 3

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

Раньше text является полем, и для локатора text, объявленного в инструкции for, для тени теневого поля, является законным.

Затем мы рассмотрим, что означает цикл for. Согласно JLS,

    for (String text : text) {...}

эквивалентно

    for (Iterator<String> #i = text.iterator(); #i.hasNext(); ) {
        String text = (String) #i.next();
        ...
    }

Как вы можете видеть, внутренний text не является видимым для выражения text.iterator().


Я попытался найти базу данных Oracle Java Bugs, но не смог найти ничего, что соответствовало бы этому сценарию.

Ответ 4

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

Очевидно, что JDK 7 анализирует цикл foreach таким образом, что переменная "текст" также находится в области видимости после ":". Чтобы проверить это, я написал следующий метод. Он компилируется и работает отлично в Java 1.7:

public static void main(String[] args) {
    for (String text : new String[] {text = "hello", text, text, text})
        System.out.println(text);
}

Хотя другие сказали, что это ошибка в jdk 1.7 (и, вероятно, это так), я не смог найти нигде в JLS, который специально говорит, что только что объявленная переменная не находится в области видимости после ":". Если это не ошибка, то Java 8 нарушает совместимость.

Ответ 5

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

Используя то же имя для коллекции, и элемент должен поднимать проблемы где-нибудь, но я слышал и иногда замечал, что Eclipse терпит то, что Sun/Oracle JDK не будет.

Ответ 6

Это скомпилировано для меня. Я использую Java 8 JDK, на Netbeans, на 64-битной машине (Windows 7).

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

false
true
false

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