当final关键字用于方法参数时,我不明白它在哪里真正方便。
如果我们排除匿名类的使用,可读性和意图声明,那么它对我来说几乎毫无价值。
强制某些数据保持不变并不像看上去那么有力。
如果参数是一个原语,那么它将没有任何影响,因为参数是作为值传递给方法的,在作用域之外更改它不会产生任何影响。
如果我们通过引用传递参数,那么引用本身就是一个局部变量,如果从方法内部更改引用,那么从方法作用域外部更改引用不会产生任何影响。
考虑下面的简单测试示例。
这个测试通过了,尽管该方法改变了给它的引用值,但它没有任何影响。
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
让我来解释一下你必须使用final的一种情况,Jon已经提到过:
如果你在你的方法中创建了一个匿名的内部类,并且在这个类中使用了一个局部变量(比如一个方法参数),那么编译器会强制你将参数设为final:
public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
return new Iterator<Integer>(){
int index = from;
public Integer next()
{
return index++;
}
public boolean hasNext()
{
return index <= to;
}
// remove method omitted
};
}
这里的from和to参数需要是final,这样它们才能在匿名类中使用。
这种需求的原因是:局部变量存在于堆栈中,因此它们只在方法执行时存在。但是,匿名类实例是从方法返回的,因此它可能存在更长的时间。不能保留堆栈,因为后续的方法调用需要它。
因此,Java所做的是将这些局部变量的副本作为隐藏的实例变量放入匿名类中(如果检查字节代码,您可以看到它们)。但如果它们不是最终的,人们可能会期望匿名类和方法看到另一个对变量所做的更改。为了保持只有一个变量而不是两个副本的错觉,它必须是最终的。
让我来解释一下你必须使用final的一种情况,Jon已经提到过:
如果你在你的方法中创建了一个匿名的内部类,并且在这个类中使用了一个局部变量(比如一个方法参数),那么编译器会强制你将参数设为final:
public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
return new Iterator<Integer>(){
int index = from;
public Integer next()
{
return index++;
}
public boolean hasNext()
{
return index <= to;
}
// remove method omitted
};
}
这里的from和to参数需要是final,这样它们才能在匿名类中使用。
这种需求的原因是:局部变量存在于堆栈中,因此它们只在方法执行时存在。但是,匿名类实例是从方法返回的,因此它可能存在更长的时间。不能保留堆栈,因为后续的方法调用需要它。
因此,Java所做的是将这些局部变量的副本作为隐藏的实例变量放入匿名类中(如果检查字节代码,您可以看到它们)。但如果它们不是最终的,人们可能会期望匿名类和方法看到另一个对变量所做的更改。为了保持只有一个变量而不是两个副本的错觉,它必须是最终的。