小编典典

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

all

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

一方面,编译器可以做一些优化,让程序员的意图更加清晰。另一方面,它增加了冗长,优化可能是微不足道的。

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


阅读 61

收藏
2022-07-28

共1个答案

小编典典

沉迷于:

  • 最终字段 - 将字段标记为最终字段会强制它们在构造结束时设置,从而使该字段引用不可变。这允许安全地发布字段,并且可以避免在以后读取时需要同步。(请注意,对于对象引用,只有字段引用是不可变的 - 对象引用所引用的内容仍然可以更改并且影响不可变性。)
  • 最终静态字段 - 虽然我现在在许多我曾经使用静态最终字段的情况下使用枚举。

考虑但明智地使用:

  • 最终类 - 框架/API 设计是我考虑的唯一情况。
  • 最终方法 - 与最终类基本相同。如果您正在使用模板方法模式(例如疯狂并将内容标记为 final),那么您可能过于依赖继承而没有足够的委托。

除非感觉肛门,否则忽略:

  • 方法参数和局部变量——我很少这样做,主要是因为我很懒,而且我发现它会使代码混乱。我完全承认我不会修改的标记参数和局部变量是“更正确的”。我希望它是默认值。但事实并非如此,而且我发现随着决赛的结束,代码更难理解。如果我在别人的代码中,我不会把它们拉出来,但如果我正在编写新代码,我不会把它们放进去。一个例外是你必须标记一些最终的东西以便你可以访问它来自匿名内部类。

  • 编辑:请注意, @adam-gent提到的最终局部变量实际上非常有用的一个用例是当值被分配给if/else分支中的 var 时。

  • final一直在使用使 Java 更加基于表达式。请参阅 Java 的条件if,else,switch

因此,在使用条件时,您应该尝试始终(恕我直言)使用最终变量。

让我给你举个例子:

    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 与 Lisp 的表达式非常相似let,并使您的代码不会大量缩进(因为词法范围变量)。

正如@Recurse 指出的那样(但显然是-1 me),您可以执行上述操作而无需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";

由于错误设置名称(除了忘记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";

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

OCaml 中的上述内容:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

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

match ... with ...评估像一个函数,即表达式。注意它看起来像我们的 switch 语句。

这是 Scheme (Racket or Chicken) 中的一个示例:

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))
2022-07-28