Java 增强的for循环编译适用于JDK 8,但不适用于JDK 7
考虑一下以下代码片段,在一些重构之后,我在签入为什么构建服务器报告了一个已损坏的构建,但在我的IDE中没有问题时,遇到了这段代码片段: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;
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
循环的表达式
您可以验证与相比,这一点没有改变
以及,
尽管两者(我试过Java6javac
)都表现出矛盾的行为
因此,编译器行为的这一变化是对一个旧错误的修复…他不是在要求解决方案,而是要求解释。虽然这是一个有趣的行为,但我也想知道,我怀疑文本和文本隐藏在同一个字段中。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);
}