Java 增强的for循环编译适用于JDK 8,但不适用于JDK 7

Java 增强的for循环编译适用于JDK 8,但不适用于JDK 7,java,foreach,java-8,java-7,language-lawyer,Java,Foreach,Java 8,Java 7,Language Lawyer,考虑一下以下代码片段,在一些重构之后,我在签入为什么构建服务器报告了一个已损坏的构建,但在我的IDE中没有问题时,遇到了这段代码片段: List<String> text; ... for (String text : text) {...} 我知道JDK在这方面做了一些改变,但是有人能告诉我为什么会发生这种行为吗 更新: 由于我得到了一些关于不同行为的评论,下面是一个完整的示例类: import java.util.Arrays; import java.util.List;

考虑一下以下代码片段,在一些重构之后,我在签入为什么构建服务器报告了一个已损坏的构建,但在我的IDE中没有问题时,遇到了这段代码片段:

List<String> text;
...
for (String text : text) {...}
我知道JDK在这方面做了一些改变,但是有人能告诉我为什么会发生这种行为吗


更新: 由于我得到了一些关于不同行为的评论,下面是一个完整的示例类:

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"));
        }
    }

}

结论:
我很困惑为什么我的IDE(使用8)没有在一条语句中抱怨两次相同的名称——但现在很明显,它不是一条语句。我真的很想知道,如果联合联络小组声明不是这样,为什么这一点已经存在这么久了。但无论如何,感谢我收到的见解和伟大的答案(这让我很难选择最好的答案)

您的生成服务器可能正在使用与本地计算机不同的jdk进行编译。(不仅仅是一个不同的版本号,而是一个完全不同的实现。)我相信Eclipse是一个使用自己的编译器来促进代码热交换的系统


对集合和元素使用相同的名称应该会在任何地方引起问题,但我听说并偶尔注意到Eclipse容忍Sun/Oracle JDK不会容忍的事情。

我认为这是您正在使用的Java 7编译器的特定版本中的一个编译器错误

前面的
text
是一个字段,在
for
语句中声明的
text
local对字段进行阴影处理是合法的

然后我们看看for循环意味着什么。据联合联络小组称

    for (String text : text) {...}
相当于

    for (Iterator<String> #i = text.iterator(); #i.hasNext(); ) {
        String text = (String) #i.next();
        ...
    }
for(迭代器#i=text.Iterator();#i.hasNext();){
字符串text=(字符串)#i.next();
...
}
如您所见,内部
文本
不在
text.iterator()
表达式的范围内



我尝试搜索Oracle Java Bugs数据库,但找不到与此场景匹配的任何内容。

这对我来说很好。我在64位机器(Windows7)上的Netbeans上使用Java8JDK

我相信这是一个与IDE或编译器相关的本地化问题。我用了你的例子,结果是

false
true
false

给出了一个警告,说明这是可能的,但不建议隐藏带有局部变量的字段。

对于JDK 7和8,这实际上应该可以编译

引用JLS(与Java 7规范相同):

增强型for语句相当于以下形式的基本for语句:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
      {VariableModifier} TargetType Identifier =
          (TargetType) #i.next();
      Statement
}
使用
迭代器重写enhanched for循环

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

for (Iterator<String> it = text.iterator(); it.hasNext(); ) {
    String text = it.next();
}
for(Iterator it=text.Iterator();it.hasNext();){
String text=it.next();
}
然后,引用JLS:

通过局部变量对成员进行跟踪的类似限制被认为是不切实际的,因为在超类中添加成员可能会导致子类必须重命名局部变量。相关的考虑使得嵌套类的成员对局部变量的阴影限制,或者嵌套类中声明的局部变量对局部变量的阴影限制也没有吸引力


因此,这里没有编译时错误,因为在用局部变量隐藏成员变量时没有限制,这里就是这种情况:局部变量
字符串文本
正在隐藏成员变量
列表文本

,尽管我认为其他答案是正确的,让我做魔鬼的代言人,提出相反的观点

显然,JDK 7解析foreach循环的方式是,变量“text”也在“:”之后的作用域中。为了测试这一点,我编写了以下方法。它在Java 1.7中编译并运行良好:

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

尽管其他人说这是JDK1.7中的一个bug(很可能是),但我在JLS中找不到任何地方明确指出刚才声明的变量不在“:”之后的范围内。如果它不是一个bug,那么Java 8破坏了兼容性。

而推理是正确的,使用从增强的
for
循环到传统的
for
循环的指定转换,使用的范围有明确的规范:

在的FormalParameter部分声明的局部变量的范围 增强的
for
语句() 是包含的语句

()

因此,变量的范围不包括增强的
for
循环的表达式

您可以验证与相比,这一点没有改变 以及, 尽管两者(我试过Java6
javac
)都表现出矛盾的行为


因此,编译器行为的这一变化是对一个旧错误的修复…

他不是在要求解决方案,而是要求解释。虽然这是一个有趣的行为,但我也想知道,我怀疑文本和文本隐藏在同一个字段中。Java8可能对此比较放松,并意识到这是循环中的两个不同变量,但Java7没有。你有没有试着重命名这个变量,使它们的名称不同?@TheLaw:反过来,它失败了7次。当然,当使用不同的名称时,两者都适用。@Mel:谢谢你指出——当然,我通常不会将两者命名为相同的名称——这只是偶然发生的,我只是在构建失败后才意识到这一点,并开始思考。我试着用JDK 8(1.8.031)编译,但我发现了错误“变量文本已在方法中定义“不,这只是让我找到了问题的根源。我的机器上有最新的2个JDK,分别是7和8,它们各自编译相同的代码,在7中得到一个编译错误,在8中没有。这与IDE或OS都没有关系。如果您确信两个测试都使用相同的JDK供应商,那么我倾向于将其作为错误报告(在1.8中),尽管我认为这是可能的
for (Iterator<String> it = text.iterator(); it.hasNext(); ) {
    String text = it.next();
}
public static void main(String[] args) {
    for (String text : new String[] {text = "hello", text, text, text})
        System.out.println(text);
}