什么时候应该对方法参数和局部变量使用final?
我发现了一些参考(例如),它们建议尽可能多地使用final
,我想知道这有多重要。这主要是在方法参数和局部变量的上下文中,而不是最终方法或者类。对于常量,这很明显。
一方面,编译器可以进行一些优化,从而使程序员的意图更加清晰。另一方面,它增加了冗长性,并且优化可能是微不足道的。
是我应该努力记住的事情吗?
解决方案
Is it something I should make an effort to remember to do?
否,如果我们使用的是Eclipse,因为我们可以配置"保存操作"来自动为我们添加这些最终修饰符。这样我们就可以以更少的精力获得收益。
好吧,这一切都取决于样式...如果我们喜欢在不修改变量的情况下看到最终版本,则可以使用它。如果我们不喜欢看到它,请忽略它。
我个人尽可能地避免冗长,因此我倾向于避免使用不必要的额外关键字。
不过,我更喜欢动态语言,因此避免冗长可能就不足为奇了。
因此,我想说的就是选择我们倾向于的方向并坚持下去(无论如何,要保持一致)。
附带说明一下,我已经在使用和不使用这种模式的项目中工作,而且我发现错误或者错误的数量没有差异……我不认为这是一个会极大地影响模式的模式改进错误计数或者其他任何方法,但这又是一种风格,如果我们喜欢表达自己不想修改的意图,请继续使用它。
正如我们提到的那样,在某种程度上需要权衡取舍,但我更喜欢显式使用某种东西而不是隐式使用。这将有助于消除代码的未来维护者的某些歧义,即使只是我们自己。
如果我们具有内部(匿名)类,并且该方法需要访问包含方法的变量,则需要将该变量作为最终变量。
除此之外,我们所说的是对的。
"最终"的开发时收益至少与运行时收益一样重要。它告诉代码的未来编辑者有关我们意图的信息。
将类标记为"最终"表示我们在类的设计或者实现过程中并未努力优雅地处理扩展。如果读者可以对课程进行更改,并希望删除"最终"修饰符,则后果自负。由他们来确保类可以很好地处理扩展。
将变量标记为" final"(并在构造函数中分配变量)对于依赖项注入很有用。它指示变量的"协作者"性质。
将方法标记为" final"在抽象类中很有用。它清楚地描述了扩展点在哪里。
痴迷于:
- 最终字段-将字段标记为最终字段会迫使它们在构造结束时进行设置,从而使该字段引用不变。这样可以安全地发布字段,并且可以避免在以后的读取中进行同步。 (请注意,对于对象引用,只有字段引用是不可变的-对象引用所引用的内容仍然可以更改,并且会影响不可变性。)
- 最终静态字段-尽管我在以前使用静态最终字段的许多情况下都使用枚举。
考虑但要谨慎使用:
- 最终课程-框架/ API设计是我唯一考虑的情况。
- 最终方法-与最终类基本相同。如果我们使用的是疯狂的模板方法模式,并且将东西标记为final,则可能是我们对继承的依赖过多,而对委派的依赖则不够。
除非感到肛门,否则忽略:
- 方法参数和局部变量-我很少这样做,主要是因为我很懒,并且发现它会使代码混乱。我将完全承认,我将不修改的标记参数和局部变量是"正确的"。我希望它是默认设置。但是事实并非如此,我发现遍历Final的代码更加难以理解。如果我使用别人的代码,则不会删除它们,但是如果我正在编写新代码,则不会放入它们。一个例外是必须标记最终内容以便可以访问它来自一个匿名内部类。
如果我们正在编写一个应用程序,例如某人将在一年后必须阅读代码,那么可以,请使用final on变量,该变量不应该一直修改。这样,代码将更加"自我记录",并且还减少了其他开发人员执行愚蠢的事情的机会,例如将局部常量用作局部临时变量。
如果我们正在编写一些一次性代码,那么,不用费心找出所有常量并将它们定为常数。
我将尽可能多地使用final。如果我们无意中更改了该字段,则将进行标记。我还将方法参数设置为final。这样做时,当我尝试"设置"参数而忘记了Java按值传递时,我从接管的代码中捕获了多个错误。
从问题尚不清楚这是否很明显,但是将方法参数定为final仅影响方法的主体。它不会将有关方法意图的任何有趣信息传达给调用者。传入的对象仍可以在方法内进行更改(最终不是const),并且变量的范围在方法内。
为了回答确切问题,除非代码需要(例如,变量是从内部类引用的),否则我不会费心地将实例或者局部变量(包括方法参数)定为final,或者澄清一些非常复杂的逻辑。
对于实例变量,如果它们在逻辑上是常数,我将使它们成为最终变量。
我发现将方法参数和本地标记为" final"可作为一种有用的重构辅助工具,当所讨论的方法是长达数页的令人费解的混乱时。自由地散布" final",查看编译器(或者IDE)抛出的"无法分配给最终变量"错误,并且即使发现了几条(过时)注释,我们也可能发现为什么名为" data"的变量最终以null结束发誓那不可能发生。
然后,我们可以通过使用在使用点附近声明的新变量替换重用的变量来修复某些错误。然后,我们发现可以将方法的整个部分都用大括号括起来,并且突然之间,我们是"提取方法"的一个IDE按键,怪物变得更加容易理解。
如果方法还不是无法解决的残骸,那么我认为将这些东西定型以阻止人们将其变成上述残骸可能是有价值的。但是,如果这是一个简短的方法(请参阅:并非不可维护),那么我们可能会冒很多冗长的风险。特别是,Java函数签名很难容纳80个字符,因为它不能为每个参数增加六个!
在参数设置中有用,可避免意外更改参数值并引入细微的错误。我过去常常忽略此建议,但花了大约4个小时。我建议我们采用一种可怕的方法(包含数百行代码和多个fors,嵌套的ifs和各种不良做法)。
public int processSomethingCritical( final int x, final int y ){ // hundreds of lines here // for loop here... int x2 = 0; x++; // bug aarrgg... // hundreds of lines there // if( x == 0 ) { ... }
当然,在完美的世界中这不会发生,但是..嗯..有时我们必须支持其他代码。 :(