在Java中,有一种做法是声明每个变量(本地或类),如果确实是参数final。
虽然这使得代码更加冗长,但这有助于轻松阅读/掌握代码,并且还可以防止错误,因为意图已明确标记。
您对此有何看法?您对此有何看法?
答案 0 :(得分:180)
痴迷:
考虑但明智地使用:
除非感觉肛门,否则忽略:
答案 1 :(得分:172)
我认为这一切都与良好的编码风格有关。当然,你可以编写好的,健壮的程序而不需要在任何地方使用很多final
修饰符,但是当你想到它时......
将final
添加到不应该更改的所有内容中,只会缩小您(或下一个程序员,处理您的代码)会误解或误用导致的思维过程的可能性在你的代码中。至少它应该响起一些钟声,因为他们现在想要改变你以前不变的东西。
首先,在您的代码中看到很多final
个关键字看起来很尴尬,但很快你就会停止注意到这个词本身并且只会想到, that-thing-will - 从这一点开始改变(你可以从我这里拿走它; - )
我认为这是一种很好的做法。我并没有一直使用它,但是当我可以并且标记某些内容final
时我会这样做。
答案 2 :(得分:32)
在使用之前,您确实需要了解final关键字的完整用法。它可以应用于变量,字段,方法和类,并且具有不同的影响
我建议查看下面链接的文章了解更多详情。
答案 3 :(得分:29)
final
修饰符,特别是对于变量,是让编译器强制执行通常合理的约定的一种方法:确保(本地或实例)变量只分配一次(不多也不少)。通过确保在使用变量之前明确赋值,可以避免NullPointerException
的常见情况:
final FileInputStream in;
if(test)
in = new FileInputStream("foo.txt");
else
System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned
通过防止多次分配变量,您可以阻止过度使用范围。而不是:
String msg = null;
for(int i = 0; i < 10; i++) {
msg = "We are at position " + i;
System.out.println(msg);
}
msg = null;
我们鼓励您使用它:
for(int i = 0; i < 10; i++) {
final String msg = "We are at position " + i;
System.out.println(msg);
}
一些链接:
答案 4 :(得分:20)
关于声明每个可能的变量final
,我非常教条。这包括方法参数,局部变量和很少的值对象字段。我有三个主要原因可以在任何地方声明最终变量:
但是,我认为最终的类和方法并不像最终变量引用那样有用。与这些声明一起使用时,final
关键字只是为自动化测试提供了障碍,并以您从未预料到的方式使用您的代码。
答案 5 :(得分:17)
Effective Java有一个项目“赞成不可变对象”。将字段声明为final可以帮助您朝着这个方向迈出一些小步骤,但对于真正不可变的对象来说当然还有更多。
如果你知道对象是不可变的,那么它们可以被共享以便在没有同步担忧的情况下在许多线程/客户端之间进行读取,并且更容易推断出程序的运行方式。
答案 6 :(得分:10)
我从来没有遇到过变量上的最终关键字阻止我犯错误的情况,所以目前我认为这是浪费时间。
除非有这样做的真正原因(因为你想要对该变量做出最终的具体观点)我宁愿不这样做,因为我发现它使代码的可读性降低。
但是,如果你没有发现它使代码更难以阅读或更长时间写,那么一定要去。
编辑:作为澄清(并尝试赢回选票),我并不是说不要将常数标记为最终,我说不要做类似的事情:
public String doSomething() {
final String first = someReallyComplicatedExpressionToGetTheString();
final String second = anotherReallyComplicatedExpressionToGetAnother();
return first+second;
}
只是让代码(在我看来)更难阅读。
还值得记住的是,所有最终确实会阻止您重新分配变量,它不会使其变为不可变或类似的东西。
答案 7 :(得分:8)
Final应始终用于常量。当定义变量的规则很复杂时,它对于短期变量(在单个方法中)甚至有用。
例如:
final int foo;
if (a)
foo = 1;
else if (b)
foo = 2;
else if (c)
foo = 3;
if (d) // Compile error: forgot the 'else'
foo = 4;
else
foo = -1;
答案 8 :(得分:5)
使用final关键字反对的最大争论之一就是“它不必要”,而且“浪费空间”。
如果我们承认这里有许多伟大帖子所指出的“最终”的诸多好处,虽然承认它需要更多的打字和空间,但我认为Java应该默认变量为“final”,并要求事情如果编码员希望它被标记为“可变”。
答案 9 :(得分:5)
我一直使用final
作为对象属性。
final
关键字在对象属性上使用时具有可见性语义。基本上,在构造函数返回之前设置最终对象属性的值。这意味着只要你不让this
引用转义构造函数并且你使用final
所有属性,你的对象就是(在Java 5语义下)保证正确构造,并且由于它也是不可变的,因此可以安全地发布到其他线程。
不可变对象不仅仅是关于线程安全的。它们还可以更容易地推断出程序中的状态转换,因为可以更改的空间是故意的,并且如果一致地使用,则仅限于应该的内容改变。
我有时也会将方法设为最终,但不是经常。我很少把课程定稿。我一般这样做是因为我没有必要这样做。我通常不会使用很多继承。我更喜欢使用接口和对象组合 - 这也适用于我发现通常更容易测试的设计。当您对接口而不是具体类进行编码时,在使用jMock等框架测试时, 不需要使用继承,使用接口创建模拟对象要容易得多它是具体的课程。
我想我应该让我的大部分课程最终成绩,但我还没有进入习惯。
答案 10 :(得分:4)
我必须为我的工作阅读很多代码。在实例变量上缺少最终结果是让我烦恼的最重要的事情之一,并使得理解代码变得不必要。对于我的钱,最终的局部变量导致比清晰度更混乱。该语言应该被设计为默认,但我们必须忍受错误。有时它特别适用于循环和if-else树的明确赋值,但大多数情况下它往往表明你的方法过于复杂。
答案 11 :(得分:3)
最终显然应该用于常量,并强制实现不变性,但方法还有另一个重要用途。
Effective Java 在此(项目15)上有一个完整的项目,指出了意外继承的缺陷。实际上,如果您没有为继承设计和记录您的类,继承它可能会产生意想不到的问题(该项提供了一个很好的示例)。因此,建议您对任何不打算从中继承的类和/或方法使用 final 。
这可能看起来很严苛,但这是有道理的。如果您正在编写一个供其他人使用的类库,那么您不希望它们继承自那些不是为它设计的东西 - 您将自己锁定到该类的特定实现中以实现后向兼容性。如果您正在团队中编码,那么除非他们确实需要,否则没有什么可以阻止团队中的其他成员删除最终。但关键字使他们思考他们正在做什么,并警告他们他们继承的类不是为它设计的,所以他们应该格外小心。
答案 12 :(得分:3)
另一个警告是,很多人混淆final意味着实例变量的内容不能改变,而不是引用不能改变。
答案 13 :(得分:2)
选择为每个方法中的每个参数键入final
会对编码员和代码阅读器产生如此大的刺激。
一旦刺激超出合理的转换到Scala,默认情况下参数是最终的。
或者,您始终可以使用自动为您执行此操作的代码样式工具。所有IDE都实现了它们或插件。
答案 14 :(得分:2)
即使对于局部变量,知道它被声明为final也意味着我不必担心稍后会更改引用。这意味着在调试时我会在稍后看到该变量,我相信它指的是同一个对象。在寻找错误时,我不必担心这一点。 一个好处是,如果99%的变量被宣布为最终变量,那么真正变量的少数变量会更好。 此外,最后一个让编译器找到一些可能会被忽视的愚蠢错误。
答案 15 :(得分:1)
我设置Eclipse以在所有未修改的字段和属性上添加final。这可以很好地使用Eclipse“保存动作”,在保存文件时添加这些最终修饰符(以及其他内容)。
强烈推荐。
查看Eclipse Save Actions的my blog post。
答案 16 :(得分:1)
我已经编写了一段时间并且尽可能地使用final。在做了一段时间后(对于变量,方法参数和类属性),我可以说90%(或更多)我的变量实际上是最终的。我认为当你不想修改变量时没有修改变量的好处(我之前看到过,而且有时很麻烦)会为代码中的额外输入和额外的“最终”关键字付出代价。
话虽如此,如果我要设计一种语言,我会将每个变量设为final,除非被其他关键字修改。
我认为,对于类和方法,我不会使用final。这是一个或多或少复杂的设计选择,除非你的类是一个实用程序类(在这种情况下你应该只有一个私有构造函数)。
我还使用Collections.unmodifiable ...在需要时创建不可修改的列表。
答案 17 :(得分:1)
对于论据,我认为他们不需要。 Mostley他们只是伤害了可读性。重新分配一个参数变量是如此愚蠢,以至于我应该相信它们可以被视为常量。
Eclipse将最终颜色变为红色这一事实使得更容易在代码中发现变量声明,我认为这些声明在大多数情况下会提高读取性。
我试图强制执行任何和所有变量都应该是最终的规则,因为没有极端的有效理由。回答“这个变量是什么?”要容易得多。问题,如果你只是必须找到启动并确信它就是它。
我实际上现在对非最终变量感到相当紧张。这就像把刀挂在头上的一根线上,或者只是把它放在你的厨房抽屉之间的差异......
最终变量只是一种很好的标记值的方法。
非最终变量绑定到一些容易出错的算法的一部分。
一个很好的特性是,当大多数时候选择在算法中使用变量时,sollution就是编写一个方法,这通常可以显着改善代码。
答案 18 :(得分:1)
我从不在局部变量上使用它们,添加的详细程度没有什么意义。即使您不认为该变量应该被重新分配,这对于下一个改变该代码的人来说也没有什么区别,并且由于代码正在被更改,因此使其成为最终的任何原始目的可能不再有效。如果只是为了清晰起见,我认为由于冗长的负面影响而失败。
同样适用于成员变量,因为它们提供的好处很少,除了常量的情况。
它也不影响不变性,因为不可变的东西的最佳指标是它被记录为这样和/或没有可以改变对象的方法(这一点,同时使类最终是唯一的方法保证它是不可变的。)
但是,嘿,这只是我的观点: - )
答案 19 :(得分:1)
最终与Java中的变量一起使用时,可以替代C ++中的常量。因此,当final和static用于变量时,它变得不可变。同时使迁移的C ++程序员非常高兴; - )
当与参考变量一起使用时,它不允许您重新引用该对象,尽管可以操纵该对象。
当final与方法一起使用时,它不允许该方法被子类覆盖。
一旦使用非常清楚,应谨慎使用。它主要取决于设计,因为使用final方法不会有助于多态性。
当你确定变量的值将/永远不会改变时,应该只将它用于变量。还要确保遵循SUN鼓励的编码约定。例如:final int COLOR_RED = 1; (大写由下划线分隔)
使用引用变量,仅在需要对特定对象的不可变引用时才使用它。
关于可读性部分,随后注释在使用final修饰符时起着非常重要的作用。
答案 20 :(得分:1)
我很难在方法或类上使用final,因为我喜欢让人们覆盖它们。
否则,如果它是public/private static final type SOME_CONSTANT;
答案 21 :(得分:0)
标记类final也可以使某些方法绑定在编译时而不是运行时发生。 考虑下面的“v2.foo()” - 编译器知道B不能有子类,因此不能覆盖foo(),因此在编译时需要调用实现。如果B类未标记为final,那么v2的实际类型可能是某个扩展B并覆盖foo()的类。
class A {
void foo() {
//do something
}
}
final class B extends A {
void foo() {
}
}
class Test {
public void t(A v1, B v2) {
v1.foo();
v2.foo();
}
}
答案 22 :(得分:0)
我将它用于方法内外的常量。
我有时只将它用于方法,因为我不知道子类是否不想覆盖给定的方法(无论出于何种原因)。
就类而言,仅针对某些基础结构类,我使用了最终类。
IntelliJ IDEA会在函数内写入函数参数时发出警告。所以,我已经停止使用final作为函数参数。我也没有在java Runtime库中看到它们。
答案 23 :(得分:0)
对事件侦听器使用匿名本地类等是Java中的常见模式。 final关键字的最常见用途是确保偶数监听器可以访问范围中的变量。
但是,如果您发现自己被要求在代码中添加大量最终语句。这可能是一个很好的暗示你做错了什么。
上面发布的文章给出了这个例子:
public void doSomething(int i, int j) {
final int n = i + j; // must be declared final
Comparator comp = new Comparator() {
public int compare(Object left, Object right) {
return n; // return copy of a local variable
}
};
}
答案 24 :(得分:-1)
强烈建议使用最终作为常量。但是,我不会将它用于方法或类(或至少考虑它一段时间),因为它使测试更难,如果不是不可能的话。如果你绝对必须让一个类或方法最终,请确保这个类实现了一些接口,这样你就可以有一个 mock 实现相同的接口。