当前位置:  开发笔记 > 编程语言 > 正文

什么时候应该使用final作为方法参数和局部变量?

如何解决《什么时候应该使用final作为方法参数和局部变量?》经验,为你挑选了8个好方法。

我发现了一些建议使用的参考文献(例如)final尽可能多地使用,我想知道它有多重要.这主要是在方法参数和局部变量的上下文中,而不是最终方法或类.对于常数,它显然是有道理的.

一方面,编译器可以进行一些优化,这使得程序员的意图更加清晰.另一方面,它增加了详细程度,优化可能是微不足道的.

这是我应该努力记住的事情吗?



1> Alex Miller..:

痴迷于:

最终字段 - 将字段标记为最终强制它们在构造结束时设置,使该字段引用不可变.这样可以安全地发布字段,并且可以避免在以后的读取时进行同步.(请注意,对于对象引用,只有字段引用是不可变的 - 对象引用引用的内容仍然可以更改并影响不变性.)

最终的静态字段 - 虽然我现在使用枚举用于我曾经使用静态最终字段的许多情况.

考虑但明智地使用:

最终类 - 框架/ API设计是我认为的唯一案例.

最终方法 - 与最终类基本相同.如果你正在使用模板方法模式,如疯狂和标记最终的东西,你可能过分依赖继承而不是委托.

忽略除非感觉肛门:

方法参数和局部变量 - 我很可能这样做很大程度上是因为我很懒,我发现它使代码混乱.我将完全承认,我不打算修改的标记参数和局部变量是"更严格".我希望这是默认的.但事实并非如此,而且我发现整个决赛的代码更难以理解.如果我在别人的代码中,我不会把它们拉出去,但是如果我正在编写新的代码,我就不会把它们放进去.一个例外是你需要标记最终的东西以便你可以访问它来自一个匿名的内部类.


尽管Josh Bloch非常尊重(这是相当数量的),但我不同意一般情况.在构建API的情况下,确保锁定.在你自己的代码中,建立你以后必须拆除的墙壁是浪费时间.
这绝对不是"浪费时间",特别是因为它根本不花费时间......在一个应用程序中,我通常默认几乎所有类都是`final`.除非您使用真正的现代Java IDE(即IDEA),否则您可能不会注意到这些好处.
IDEA(开箱即用)有数百个代码检查,其中一些可以检测`final`类/方法中未使用/不必要的代码.例如,如果final方法声明抛出一个已检查的异常但从未实际抛出它,则IDEA会告诉您,并且您可以从`throws`子句中删除该异常.有时,您还可以找到未使用的整个方法,这些方法在无法覆盖时是可检测的.
Joshua Bloch认为所有类都应该定义为final,除非它们是为继承而设计的.我同意他的看法; 我将final添加到实现接口的每个类中(以便能够创建单元测试).还要标记为最终所有受保护/类方法,这些方法不会被覆盖.
@rmaruszewski将类标记为final可以防止大多数模拟框架能够模拟它们,从而使代码更难以测试.如果没有扩展,那么我只会进行一次决赛.
Java应该刚刚做出最终的默认值!uggg!
我想要一个Java编辑器"更改"方法参数和局部变量的默认值:它应该隐藏声明中的final关键字,并突出显示没有final的声明.不幸的是,我认为这样的编辑器不存在.Eclipse中有[此功能的请求](https://bugs.eclipse.org/bugs/show_bug.cgi?id=409379).
好的,我会咬人的.您在IntelliJ中获得了哪些好处?
Eclipse有一些相同的检查(可能也是NetBeans).我会认为这是一个好处,但我不相信这是一个大问题.

2> Peter Hilton..:

这是我应该努力记住的事情吗?

不,如果您正在使用Eclipse,因为您可以配置"保存操作"以自动为您添加这些最终修饰符.然后,您可以轻松获得收益.


这真的是一个问题吗?您实际上有多少次因此导致了错误?
保存动作的重要提示,不知道这一点.

3> Eric R. Rath..:

"最终"的开发时间优势至少与运行时间优势一样重要.它告诉未来的编辑代码有关你的意图.

将类标记为"final"表示您在设计或实现类时没有努力优雅地处理扩展.如果读者可以对课程进行更改,并希望删除"最终"修饰符,则他们可以自行承担风险.这取决于他们确保课程能够很好地处理扩展.

标记变量"final"(并在构造函数中赋值)对依赖项注入很有用.它表示变量的"协作者"性质.

标记方法"final"在抽象类中很有用.它清楚地描绘了扩展点的位置.



4> Sam Stokes..:

我发现标记方法参数和局部作为final重构辅助工具,当有问题的方法是一个难以理解的混乱几页长.final自由地撒上,看看编译器(或你的IDE)抛出的"无法分配给最终变量"的错误,你可能会发现为什么名为"data"的变量最终为null,即使几个(过时的)注释发誓可以不会发生.

然后,您可以通过将重用的变量替换为更接近使用点的新变量来修复一些错误.然后你会发现你可以将方法的整个部分包装在范围大括号中,突然间你只需要一个远离"提取方法"的IDE键盘,你的怪物就会更容易理解.

如果你的方法不是一个不可维护的残骸,我想最终可能会有一些东西可以阻止人们把它变成残骸.但如果它是一个简短的方法(参见:不是不可维护的),那么你冒着增加大量冗长的风险.特别是,Java函数签名非常难以适应80个字符,而不是每个参数增加6个字符!



5> Adam Gent..:

我一直用final它来使Java更基于表达式.请参阅Java的条件(if,else,switch)不是基于表达式的,我一直很讨厌,特别是如果您习惯于函数式编程(即ML,Scala或Lisp).

因此,在使用条件时,您应该尝试始终(IMHO)使用最终变量.

让我给你举个例子:

    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编译器将失败.如果你不打破每个案例(你设置变量),编译器也会失败.这使得Java可以使Java与Lisp的let表达式非常相似,并使得代码不会大量缩进(因为词法范围变量).

正如@Recurse所指出的那样(但显然是-1我)你可以完成前面的工作String name final来获取编译器错误(我从来没有说过你不能)但你可以很容易地让编译错误消失后设置名称抛出表达语义的语句或更糟糕的忘记break你不能导致错误(尽管@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";

由于bug设置名称(除了忘记break还有另一个bug)我现在可以意外地执行此操作:

    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";

最终变量强制单一评估应该是什么名称.类似于具有返回值的函数必须始终返回一个值(忽略异常),名称切换块必须解析名称,从而绑定到该切换块,这使得重构代码块变得更容易(即Eclipe重构:提取方法) .

OCaml中的上述内容:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

match ... with ...像的功能,即表达式的计算结果.注意它看起来像我们的switch语句.

这是Scheme(球拍或鸡肉)中的一个例子:

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))


是的但是在决赛中你可以随时重置它.我实际上已经看到这种情况发生在开发人员将`else if(...)`转换为`if(...)`并因此重置变量.我向他展示了最终变量永远不会发生的事情.基本上`final`强制你只分配一次变量......所以:P

6> Mike Stone..:

好吧,这一切都取决于你的风格...如果你想看到最后你不会修改变量,那么使用它.如果你不喜欢看到它...然后把它留下来.

我个人喜欢尽可能少的冗长,所以我倾向于避免使用非必要的额外关键字.

我喜欢动态语言,所以我喜欢避免冗长也就不足为奇了.

所以,我会说只是选择你倾向的方向,然后继续使用它(无论如何,尽量保持一致).


作为旁注,我参与了使用和不使用这种模式的项目,我发现错误或错误的数量没有差别...我不认为这是一个巨大的模式改进你的错误计数或任何东西,但同样它是风格,如果你喜欢表达你不会修改它的意图,那么继续使用它.



7> OscarRyz..:

在参数中有用是为了避免意外更改参数值并引入一个微妙的错误.我用了忽略这个建议,但花了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 ) { ...

 }

当然,在一个完美的世界中,这不会发生,但是......好吧..有时你必须支持其他代码.:(


这种方法比最终缺失更严重.虽然并非不可能,但是有一个很好的理由让方法如此混乱以至于会出现这些类型的错误.对变量名称进行一点思考会对这类事故大有帮助.
如果你在一个方法中有"数百行代码",你可能想把它分解成几个较小的方法.

8> Alvin..:

如果你正在编写一个应用程序,有人必须在1年之后读取代码,那么是的,使用final不应该一直修改的变量.通过这样做,您的代码将更加"自我记录",并且您还可以减少其他开发人员使用本地常量作为本地临时变量等愚蠢行为的机会.

如果你正在编写一些一次性代码,那么,不,不要费心去识别所有的常量并使它们成为最终的.

推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有