Java foreach循环中的字段赋值

3

我知道在下面的例子中使用的foreach循环是不会编译的。但是有人知道为什么在foreach循环声明中使用字段是不允许的吗?

public class Foo {
    private Object obj;

    public void run(List<Object> objects) {
        for (obj : objects) {
            process();
        }
    }

    private void process() {
        // do something with obj
    }
}
5个回答

7

可能是因为它

  • 限制了循环变量的范围(从而使代码更清晰,避免可能的微妙错误),并且
  • 简化了编译器的解析。

我想知道设计师是否也考虑到了将修改后的for循环未来扩展为并行执行循环变量不同值的主体。如果变量存在于循环之外,那么在不实现第三种类型的for循环的情况下,添加此功能将会很困难。 - Rich
3
@Rich:无论如何,现在将其纳入当前的语义中都很困难,因为您当前保证元素的顺序与集合迭代器的顺序相同。我认为并行化要么会破坏这一点,要么通过过度同步变得无用。 - Mark Peters
@Mark,考虑到元素访问顺序的保证,这很有道理。我一直对循环的顺序感到不安。谢谢您减轻了我的负担。 - Rich

2

在foreach循环中给字段赋值通常没有意义。在foreach循环中,对象的作用域仅限于循环的一次迭代,而字段的作用域是对象的生命周期。

你应该只将每个对象作为参数传递给process()...将引用存储为字段不会带来任何好处。此外,如果你真的想这样做,你可以使用this.obj = obj手动给字段赋值。


我同意字段描述对象的状态。然而,在Web应用程序中,如果类的实例仅在请求期间存在,则使用字段而不是局部变量是完全可以的。 - kraftan
1
@kraftan 问题在于,循环本身仍然局限于单个方法调用。因此,在循环中使用的变量也应该是局部的。最小化事物(包括变量)的范围是良好的设计,考虑到这可能引入的许多潜在问题(如果不使用字段,则可能会出现可怕的线程安全问题),他们明智地选择不允许这样的事情。我怀疑甚至没有考虑过。 - ColinD
我同意。通常不允许这样的事情绝对是明智的。 - kraftan

1

我认为有几个原因,但最终可能只是为了防止程序员出错。

一个令人困惑的问题是,“在循环执行后,obj的值将是多少?”与标准的for循环不同,增强型for-each循环并不试图保证其自身的机制。

另一个问题是实例字段表示对象的状态。在for-each循环中使用实例字段所表达的意思是,对象可以在单个操作期间从一个状态变化到一个或多个中间状态,然后到达最终状态。这是一种糟糕的设计,值得避免。

为什么不将obj作为参数传递给process()呢?


1
在 for 循环运行之前,obj 应该具有什么值?循环结束后,它应该具有什么值?这可能会令人困惑。

这可能与普通for循环的循环变量相同:在循环之前,它具有进入循环之前的任何值; 在循环之后,它具有退出循环之前的最后一个值。并不是说这是一个好主意还是坏主意,实际上我倾向于赞同@TedHopp答案中提到的回应。我只是说它会像普通的for循环一样令人困惑或不困惑。 - SantiBailors

1

关于这个问题,早在2004年就有错误报告。但是被拒绝了,回应很有趣:

强制循环变量在循环内部声明(请参见JLS 14.14.2中的翻译),采取了某种“安全第一”的方法。首先,不存在意外覆盖封闭作用域中的变量的危险。其次,每次迭代使用新的循环变量可以避免如果循环体将变量传递给其他线程而引起的并发问题。第三,编译器可以通过将循环变量声明为“final”来进行优化。第四,如果封闭作用域中的变量被重用作循环变量,并且程序员认为循环遍历整个集合,他们可能希望能够通过循环变量观察到迭代后的最后一个元素。但它不一定指向最后一个元素,例如如果循环执行了break。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接