参考书目 《Java安全编码标准》
3.3 DCL02-J将所有增强for语句的循环变量声明为final类型
Java 5平台(也因for-each风格出名)引入了增强的for语句,它用来对对象集合进行迭代。与基本的for语句不同,在基本的for语句中,给循环变量赋值是不能对循环的迭代次序有所影响。但在增强的for语句中,给循环变量赋值就可以有影响,而不是像程序员通常认为的那样。这使我们认识到应避免给在for循环中的循环变量赋值。
详情请见JLS的14.14.2节“增强的for语句”[JLS 2005]。
一个增强的for循环通常采用以下这种形式:
for (ObjType obj : someIterableItem) { // ... }
这等价于以下形式的基本for循环:
for (Iterator myIterator = someIterableItem.iterator(); myIterator.hasNext();) { ObjType obj = myIterator.next(); // ... }
所以,一个给循环变量赋值的动作,等价于修改循环体的局部变量的值,这个变量的初始值会被循环迭代器引用。这种修改不一定是错误的,但它会模糊循环的行为,或者意味着对增强型for语句的内在实现,这在理解上有问题。
可以将所有的for语句中的循环变量声明为final。这个final声明可以让Java编译器做一个标志,并且拒绝对这个循环变量的任何赋值。
3.3.1 不符合规则的代码示例
这个不符合规则的代码示例想要使用增强型的for循环处理对象集合。此外,它还希望跳过对集合中某一个元素的处理。
Collection<ProcessObj> processThese = // ...? for (ProcessObj processMe: processThese) { if (someCondition) { // found the item to skip someCondition = false; processMeprocessMe = processMe.getNext(); // attempt to skip to next item } processMe.doTheProcessing(); // process the object }
这种跳过到下一个集合元素的想法看起来是可以实现的,因为已经成功赋值,并且processMe变量的值也更新了。然而,和基本型的for循环不同,这个赋值并没有改变循环执行的迭代次序。因此,虽然需要跳过的元素跳过了,但是它之后的元素被处理了两次。
注意,如果声明processMe为final,在试图对它进行这样的赋值时,就会产生编译器错误。
3.3.2 符合规则的方案
这个符合规则的方案可以正确地处理在集合中的每一个对象,并且每一个对象只会被处理一次。
Collection<ProcessObj> processThese = // ...? for (final ProcessObj processMe: processThese) { if (someCondition) { // found the item to skip someCondition = false; continue; // skip by continuing to next iteration } processMe.doTheProcessing(); // process the object }
转载于:https://blog.51cto.com/jixiaochong/1762666