我不明白其中final
的关键字是真的方便的时候它是在方法的参数使用.
如果我们排除使用匿名类,可读性和意图声明,那么对我来说似乎几乎一文不值.
强制某些数据保持不变并不像看起来那么强大.
如果参数是基元,那么它将没有任何效果,因为参数作为值传递给方法,并且更改它将在范围之外没有任何影响.
如果我们通过引用传递参数,那么引用本身就是一个局部变量,如果从方法中更改引用,那么从方法范围外部不会产生任何影响.
考虑下面的简单测试示例.尽管该方法改变了给定的参考值,但该测试仍然通过,但它没有效果.
public void testNullify() { Collectionc = new ArrayList (); nullify(c); assertNotNull(c); final Collection c1 = c; assertTrue(c1.equals(c)); change(c); assertTrue(c1.equals(c)); } private void change(Collection c) { c = new ArrayList (); } public void nullify(Collection> t) { t = null; }
Jerry Sha.. 228
有时显而易见(为了可读性)变量不会改变.这是一个简单的例子,使用final可以节省一些可能的麻烦
public void setTest(String test) { test = test; }
如果你在setter上忘记了'this'关键字,那么你想要设置的变量就不会被设置.但是,如果您在参数上使用了final关键字,则会在编译时捕获该错误.
有时显而易见(为了可读性)变量不会改变.这是一个简单的例子,使用final可以节省一些可能的麻烦
public void setTest(String test) { test = test; }
如果你在setter上忘记了'this'关键字,那么你想要设置的变量就不会被设置.但是,如果您在参数上使用了final关键字,则会在编译时捕获该错误.
虽然这些答案在理智上很有趣,但我还没有读到简短的简单答案:
如果希望编译器阻止将变量重新分配给其他对象,请使用关键字final.
无论变量是静态变量,成员变量,局部变量还是参数/参数变量,效果都是完全相同的.
让我们看看效果如何.
考虑这个简单的方法,其中两个变量(arg和x)都可以重新分配不同的对象.
// Example use of this method: // this.doSomething( "tiger" ); void doSomething( String arg ) { String x = arg; // Both variables now point to the same String object. x = "elephant"; // This variable now points to a different String object. arg = "giraffe"; // Ditto. Now neither variable points to the original passed String. }
将局部变量标记为final.这会导致编译器错误.
void doSomething( String arg ) { final String x = arg; // Mark variable as 'final'. x = "elephant"; // Compiler error: The final local variable x cannot be assigned. arg = "giraffe"; }
相反,让我们将参数变量标记为final.这也会导致编译器错误.
void doSomething( final String arg ) { // Mark argument as 'final'. String x = arg; x = "elephant"; arg = "giraffe"; // Compiler error: The passed argument variable arg cannot be re-assigned to another object. }
故事的道德启示:
如果要确保变量始终指向同一对象,请将变量标记为final.
由于良好的编程习惯(用任何语言),你应该永远参数/参数变量重新分配给比调用方法传递的对象以外的对象.在上面的例子中,永远不应该写行arg =
.由于人类犯错误,程序员是人类,我们请求编译器协助我们.将每个参数/参数变量标记为"final",以便编译器可以查找并标记任何此类重新分配.
正如其他答案所述......鉴于Java的原始设计目标是帮助程序员避免愚蠢的错误,例如读取数组的末尾,Java应该被设计为自动将所有参数/参数变量强制执行为"final".换句话说,参数不应该是变量.但事后看来是20/20的愿景,Java设计师当时已经满满的.
为完整性添加了另一个案例public class MyClass { private int x; //getters and setters } void doSomething( final MyClass arg ) { // Mark argument as 'final'. arg = new MyClass(); // Compiler error: The passed argument variable arg cannot be re-assigned to another object. arg.setX(20); // allowed // We can re-assign properties of argument which is marked as final }
是的,排除匿名类,可读性和意图声明它几乎一文不值.这三件事虽然毫无价值吗?
我个人倾向于不使用final
局部变量和参数,除非我在匿名内部类中使用变量,但我当然可以看到那些想要明确参数值本身不会改变的人的观点(甚至如果它引用的对象改变其内容).对于那些发现增加可读性的人,我认为这是完全合理的事情.
如果有人真的声称它确实以不支持的方式保持数据不变,那么你的观点将更为重要- 但我不记得看到任何此类声明.您是否建议有大量开发人员认为其final
效果比实际更有效?
编辑:我应该用Monty Python参考总结所有这些; 这个问题看起来有点类似于问"罗马人为我们做过什么?"
让我解释一下你必须使用final 的一个案例,Jon已经提到过:
如果在方法中创建匿名内部类并在该类中使用局部变量(例如方法参数),则编译器会强制您使参数最终:
public IteratorcreateIntegerIterator(final int from, final int to) { return new Iterator (){ int index = from; public Integer next() { return index++; } public boolean hasNext() { return index <= to; } // remove method omitted }; }
这里from
和to
参数需要是最终的,因此它们可以在匿名类中使用.
这个要求的原因是:局部变量存在于堆栈中,因此它们仅在执行方法时存在.但是,从该方法返回匿名类实例,因此它可能会存活更长时间.您无法保留堆栈,因为后续方法调用需要它.
所以Java所做的就是将这些局部变量的副本作为隐藏实例变量放入匿名类中(如果检查字节代码,可以看到它们).但是如果它们不是最终的,那么人们可能会期望匿名类和方法看到另一个对变量做出的改变.为了保持只有一个变量而不是两个副本的错觉,它必须是最终的.
我一直在使用final参数.
它增加了那么多吗?并不是的.
我会把它关掉吗?没有.
原因是:我发现3个错误,其中人们编写了草率代码并且无法在访问器中设置成员变量.事实证明,所有的漏洞都很难找到.
我希望看到这在未来的Java版本中成为默认值.价值/参考物的传递使很多初级程序员绊倒了.
还有一件事......我的方法往往具有较少的参数,因此方法声明中的额外文本不是问题.
在方法参数中使用final与调用者方面的参数发生的事情无关.它只是意味着它在该方法内部没有变化.当我尝试采用更具功能性的编程风格时,我看到了它的价值.
我个人不会在方法参数上使用final,因为它会给参数列表增加太多的混乱.我更喜欢强制执行方法参数不会像Checkstyle这样改变.
对于局部变量,我尽可能使用final,我甚至让Eclipse在我的个人项目设置中自动执行.
我当然喜欢像C/C++ const这样强大的东西.