什么时候应该使用final方法参数和局部variables?
我发现了几个参考( 例如 ),build议尽可能使用final
,我想知道这是多么重要。 这主要是在方法参数和局部variables的上下文中,而不是最终的方法或类。 对于常量来说,这是显而易见的。
一方面,编译器可以进行一些优化,并使程序员的意图更清晰。 另一方面,它增加了冗长,优化可能是微不足道的。
我应该努力记住这件事吗?
迷恋:
- 最后的领域 – 标记领域作为最后的力量他们被设置结束build设,使该领域的参考不可变的。 这允许安全地发布字段并且可以避免在稍后的读取中同步的需要。 (请注意,对于对象引用,只有字段引用是不可变的 – 对象引用引用的内容仍然可以更改,并影响不可变性。)
- 最终静态字段 – 虽然我现在使用枚举现在很多情况下,我曾经使用静态最终字段。
考虑一下,但明智地使用:
- 最终课程 – 框架/ APIdevise是我考虑的唯一案例。
- 最后的方法 – 基本上和最终的课程一样。 如果你使用模板方法模式,如疯狂和最后标记的东西,你可能过于依赖inheritance,而不是足够的委派。
忽略除非感觉肛门:
- 方法参数和局部variables – 我很less这样做主要是因为我很懒,我发现它混乱的代码。 我将完全承认,我不会修改的标记参数和局部variables是“较为”。 我希望这是默认的。 但事实并非如此,我发现整个决赛更难以理解代码。 如果我在别人的代码中,我不会把它们拉出来,但是如果我正在编写新的代码,我不会把它们放进去。一个例外是,你必须标记最后的东西,所以你可以访问它来自一个匿名的内部类。
我应该努力记住这件事吗?
不,如果你使用Eclipse,因为你可以configuration一个保存动作来为你自动添加这些最终修饰符。 那么你就可以less花点功夫
“最终”的开发时间效益至less和运行时间效益一样重要。 它告诉未来编辑的代码有关你的意图。
标记一个类“final”意味着你没有在devise或实现类的时候努力正确地处理扩展。 如果读者可以对class级进行修改,并想删除“最终”修饰语,他们可以自行承担风险。 确保class级能够很好地处理延期,这取决于他们。
标记variables“final”(并在构造函数中赋值)对dependency injection非常有用。 它表明variables的“协作者”性质。
在抽象类中标记方法“final”是有用的。 它清楚地描述了扩展点的位置。
我发现标记方法参数和当地人作为final
是有用的重构援助时,有问题的方法是一个难以理解的混乱几页长。 final
,我们可以宽泛地看到编译器(或者IDE)抛出什么“不能分配给最终variables”的错误,而且你可能会发现为什么称为“data”的variables结束为null,即使有几条(过时的)不能发生。
然后你可以修改一些错误,把重用的variablesreplace成更接近于使用点的新variables。 然后你会发现你可以将方法的所有部分包装在范围大括号中,而突然间,你就是一个IDE按键,而不是“提取方法”,而你的怪物才会更容易理解。
如果你的方法不是一个不可维护的残骸,那么我认为在最终决定什么东西时可能是有价值的,以防止人们把它变成沉船; 但如果这是一个简短的方法(请参阅:不可维护),那么您可能会添加大量冗长的内容。 特别是,Java函数签名非常难以容纳80个字符,而不需要每个参数增加6个字符!
那么,这一切都取决于你的风格…如果你看到最后的时候你不会修改variables,然后使用它。 如果你不喜欢看到它,那就离开它。
我个人喜欢尽可能小的冗长,所以我倾向于避免使用额外的关键字,这是不必要的。
我更喜欢dynamic语言,所以我可能不会奇怪,我喜欢避免冗长。
所以,我想说的就是select你正在倾向的方向,然后随它而行(无论如何,尽量保持一致)。
作为一个侧面说明,我曾经在使用和不使用这种模式的项目上工作过,而且我没有看到错误或错误的数量上的差异…我不认为这是一个巨大的模式提高你的bug数量或任何东西,但它又是风格,如果你想expression意图,你不会修改它,然后继续使用它。
如果你正在编写一个应用程序,那么在一年之后有人需要阅读代码,那么是的,对于不应该一直修改的variables使用final。 通过这样做,你的代码将会更加“自我logging”,同时你也可以减less其他开发者做一些愚蠢的事情,比如使用本地常量作为本地临时variables。
如果你正在写一些一次性的代码,那么,不要麻烦确定所有的常量,并把它们做成最终的结果。
在参数中避免意外更改参数值并引入一个细微的错误是很有用的。 我使用忽略这个build议,但花了大约4小时后。 在一个可怕的方法(数百行代码和多个fors,嵌套ifs和各种不好的做法),我会build议你这样做。
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 ) { ... }
当然,在一个完美的世界里,这不会发生,但是..有时候你必须支持别人的代码。 🙁
我一直在使用final
来使Java更加基于expression式。 看Java的条件( if,else,switch
)不是基于expression式,我一直恨,特别是如果你习惯于函数式编程(即ML,Scala或Lisp)。
因此,您应该尝试始终(恕我直言)在使用条件时使用最终variables。
让我举一个例子:
final String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; case JOB_POSTING_IMPORT: name = "Blah"; break; default: throw new IllegalStateException(); }
现在,如果添加另一个case
语句并且不设置name
,编译器将会失败。 编译器也将失败,如果你不打破每一个案件(你设置variables)。 这使得你可以使Java非常类似于Lisp的let
expression式,并且使得你的代码不会被大量缩进(因为词法范围variables)。
和@Recurse指出的(但显然是-1我)你可以做出前面的String name
final
得到编译器错误(我从来没有说过你不能),但你可以很容易地使编译器错误消失设置名称在switch语句抛出expression式语义或更糟糕的忘记break
你不能导致错误(尽pipe@Recurse说)而不使用final
:
String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; //break; whoops forgot break.. //this will cause a compile error for final ;P @Recurse case JOB_POSTING_IMPORT: name = "Blah"; break; } // code, code, code // Below is not possible with final name = "Whoops bug";
由于错误设置的名称(除了忘记break
哪个也是另一个错误),我现在可以不小心做到这一点:
String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; //should have handled all the cases for pluginType } // code, code, code // Below is not possible with final name = "Whoops bug";
最后的variables强制一个单一的评估应该是什么名字。 与具有返回值的函数必须始终返回一个值(忽略exception)类似,名称开关块必须parsing名称,并绑定到使代码重构更容易的开关块(即Eclipe重构:提取方法) 。
以上在OCaml中:
type plugin = CandidateExport | JobPostingImport let p = CandidateExport let name = match p with | CandidateExport -> "Candidate Stuff" | JobPostingImport -> "Blah" ;;
match ... with ...
评估就像一个函数,即expression式。 注意它看起来像我们的switch语句。
scheme(球拍或鸡肉)中有一个例子:
(define name (match b ['CandidateExport "Candidate Stuff"] ['JobPostingImport "Blah"]))
我会尽可能地使用最终的。 这样做会标志,如果你无意中改变领域。 我也将方法参数设置为final。 这样做,我已经从我接pipe的代码中捕获了几个错误,当他们尝试“设置”一个忘记Java通过值的参数。
从这个问题是否显而易见,但是使方法参数最终只影响方法的主体还不清楚。 它没有向调用者传达任何有关该方法意图的有趣信息。 传入的对象仍然可以在方法内部进行变化(最终不是常量),variables的作用域在方法中。
为了回答你的确切问题,除非代码需要它(例如variables是从一个内部类引用的),否则我不会打扰做一个实例或局部variables(包括方法参数),或者澄清一些非常复杂的逻辑。
例如variables,如果他们是逻辑常量,我会让他们最终。
variablesfinal
有很多用途。 这里仅仅是less数
最终常数
public static class CircleToolsBetter { public final static double PI = 3.141; public double getCircleArea(final double radius) { return (Math.pow(radius, 2) * PI); } }
这可以用于代码的其他部分,或者由其他类访问,这样,如果你改变了值,你将不必一一改变它们。
最终variables
public static String someMethod(final String environmentKey) { final String key = "env." + environmentKey; System.out.println("Key is: " + key); return (System.getProperty(key)); } }
在这个类中,你build立一个范围最终variables,为参数environmentKey添加一个前缀。 在这种情况下,最终variables只在执行范围内是最终的,这在每次执行方法时是不同的。 每次input方法,最后都被重build。 一旦build成,在方法执行的范围内不能改变。 这允许您在方法的持续时间内修复方法中的variables。 见下文:
public class FinalVariables { public final static void main(final String[] args) { System.out.println("Note how the key variable is changed."); someMethod("JAVA_HOME"); someMethod("ANT_HOME"); } }
最终常数
public double equation2Better(final double inputValue) { final double K = 1.414; final double X = 45.0; double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M); double powInputValue = 0; if (result > 360) { powInputValue = X * Math.sin(result); } else { inputValue = K * Math.sin(result); // <= Compiler error }
当你有很长的代码行时,这些特别有用,它会产生编译器错误,所以当有人不小心改变了不应该被改变的variables时,你不会遇到逻辑/业务错误。
最终collections
不同的情况下,当我们谈论集合,你需要把它们设置为一个不可修改的。
public final static Set VALID_COLORS; static { Set temp = new HashSet( ); temp.add(Color.red); temp.add(Color.orange); temp.add(Color.yellow); temp.add(Color.green); temp.add(Color.blue); temp.add(Color.decode("#4B0082")); // indigo temp.add(Color.decode("#8A2BE2")); // violet VALID_COLORS = Collections.unmodifiableSet(temp); }
否则,如果你不把它设置为不可修改的:
Set colors = Rainbow.VALID_COLORS; colors.add(Color.black); // <= logic error but allowed by compiler
最终类和最终方法不能被分别扩展或覆盖。
编辑:解决有关封装的最终类问题:
有两种方法可以进行课堂决赛。 首先是在类声明中使用关键字final:
public final class SomeClass { // . . . Class contents }
做出类最后的第二种方法是将其所有构造函数声明为私有的:
public class SomeClass { public final static SOME_INSTANCE = new SomeClass(5); private SomeClass(final int value) { }
如果发现它实际上是最后一个,那么最后标记它可以节省你的麻烦,以便展示一下这个Test类。 乍一看是公开的。
public class Test{ private Test(Class beanClass, Class stopClass, int flags) throws Exception{ // . . . snip . . . } }
不幸的是,由于这个类的唯一构造函数是私有的,所以不可能扩展这个类。 在Test类的情况下,没有理由说这个类应该是final的。 Test类是隐式最终类如何导致问题的一个很好的例子。
所以你应该把它标记为final,当你隐式地做一个类的最终使它的构造函数是私有的。
你提到的有点权衡,但我更喜欢显式使用某些东西而不是隐式使用。 这将有助于消除未来代码维护者的一些模糊性 – 即使它只是你。
如果您有内部(匿名)类,并且该方法需要访问包含方法的variables,则需要将该variables作为最终的variables。
除此之外,你所说的是对的。
如果您将该variables设置为immutable
,请使用final
关键字作为variables
通过将variables声明为final,它帮助开发人员在高度multithreading的环境中排除可能的variables修改问题。
随着Java 8的发布,我们有一个更多的概念被称为“ effectively final variable
”。 非最终variables可以作为最终variables。
从lambdaexpression式引用的局部variables必须是最终的或有效的最终的
如果在本地块中初始化后没有修改variables,则认为该variables是有效的 。 这意味着您现在可以在匿名类或lambdaexpression式中使用不带final关键字的本地variables,只要它们必须是有效的最终的。
在Java 7中,不能在匿名类中使用非最终本地variables,但是可以从Java 8中使用
看看这篇文章
一个非常简单的答案,我们有三个最终与variables,最终与方法&&最终与类..
如果uu不能多次赋值这个variables
最后用方法你不能重写这个方法。
最后一堂课不能延续最后一堂课
首先,最后的关键字用于使variables不变。 常量意味着它不会改变。 例如:
final int CM_PER_INCH = 2.54;
你会声明variablesfinal,因为厘米每英寸不会改变。
如果您尝试覆盖最终值,则该variables就是它首先被声明的内容。 例如:
final String helloworld = "Hello World"; helloworld = "A String"; //helloworld still equals "Hello World"
有一个编译错误是这样的:
local variable is accessed from inner class, must be declared final
如果你的variables不能被声明为final,或者你不想声明它,那么试试这个:
final String[] helloworld = new String[1]; helloworld[0] = "Hello World!"; System.out.println(helloworld[0]); helloworld[0] = "A String"; System.out.println(helloworld[0]);
这将打印:
Hello World! A String