首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么我要在Java中的方法参数上使用关键字"final“?

为什么我要在Java中的方法参数上使用关键字"final“?
EN

Stack Overflow用户
提问于 2009-02-01 09:49:43
回答 12查看 166.2K关注 0票数 427

当在方法参数上使用final关键字时,我不能理解它在哪里真正方便。

如果我们排除匿名类的使用,可读性和意图声明,那么它对我来说似乎几乎毫无价值。

强制要求某些数据保持不变并不像看起来那么有力。

  • 如果参数是原语,那么它将不起作用,因为参数是作为值传递给方法的,并且在作用域之外更改它不会产生任何效果。
  • 如果我们通过引用传递参数,那么引用本身就是一个局部变量,如果在方法内部更改了引用,那么在方法作用域之外就不会有任何影响。

考虑下面这个简单的测试示例。这个测试通过了,尽管该方法更改了提供给它的引用的值,但它没有任何效果。

public void testNullify() {
    Collection<Integer> c  = new ArrayList<Integer>();      
    nullify(c);
    assertNotNull(c);       
    final Collection<Integer> c1 = c;
    assertTrue(c1.equals(c));
    change(c);
    assertTrue(c1.equals(c));
}

private void change(Collection<Integer> c) {
    c = new ArrayList<Integer>();
}

public void nullify(Collection<?> t) {
    t = null;
}
EN

回答 12

Stack Overflow用户

发布于 2012-04-30 16:20:56

停止变量的重新赋值

虽然这些答案在智力上很有趣,但我没有读过简短而简单的答案:

当您希望编译器防止变量被重新赋值给不同的对象时,可以使用关键字

无论变量是静态变量、成员变量、局部变量还是实参/参数变量,效果都是完全相同的。

示例

让我们来看看实际效果。

考虑这个简单的方法,其中两个变量(arg和x)都可以重新分配不同的对象。

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

将局部变量标记为最终变量。这会导致编译器错误。

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

相反,让我们将参数变量标记为final。这也会导致编译器错误。

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

这个故事的寓意:

如果要确保变量始终指向同一对象,请将变量标记为final。

从不重新分配参数

作为良好的编程实践(在任何语言中),您不应该将参数/实参变量重新分配给调用方法传递的对象以外的对象。在上面的例子中,永远不应该写arg =这一行。既然人类会犯错,程序员也是人,让我们请求编译器来帮助我们。将每个参数/实参变量标记为'final‘,以便编译器可以找到并标记任何这样的重新赋值。

回顾

如other answers…中所述考虑到Java最初的设计目标是帮助程序员避免愚蠢的错误,比如读过数组的结尾,Java应该被设计为自动将所有参数/实参变量强制为“final”。换句话说,参数不应该是变量。但是后见之明是20/20的远见,Java设计者当时忙得不可开交。

那么,总是将final添加到所有参数吗?

我们是否应该将final添加到每个声明的方法参数中?

  • 在理论上是这样的,而
  • 在实践中是不这样的。

仅当方法的代码过长或复杂时才添加final,其中的参数可能被误认为是局部变量或成员变量,也可能是re-assigned.

如果您采用从不重新赋值参数的做法,您将倾向于为每个参数添加一个final。但这很乏味,而且使声明更难阅读。

对于简短的简单代码,其中的参数显然是一个参数,而不是局部变量或成员变量,我不会费心添加final。如果代码非常明显,我和任何其他程序员都不可能意外地将参数变量误认为参数以外的其他变量,那么就不必费心了。在我自己的工作中,我只在更长或更复杂的代码中添加final,在这些代码中,参数可能会被误认为是局部变量或成员变量。

#为完整性添加了另一种情况

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.
  
   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }

record

Java16引入了新的records特性。记录是定义一个类的一种非常简短的方法,该类的中心目的仅仅是以不变和透明的方式携带数据。

您只需声明类名及其成员字段的名称和类型。编译器隐式地提供构造函数、getters、equals & hashCodetoString

这些字段是只读的,没有setter。因此,record是一种不需要将参数标记为final的情况。它们实际上已经是最终版本了。实际上,编译器禁止在声明记录的字段时使用final

public record Employee( String name , LocalDate whenHired )  //  Marking `final` here is *not* allowed.
{
}

如果你提供了一个可选的构造函数,你可以在那里标记为final

public record Employee(String name , LocalDate whenHired)  //  Marking `final` here is *not* allowed.
{
    public Employee ( final String name , final LocalDate whenHired )  //  Marking `final` here *is* allowed.
    {
        this.name = name;
        whenHired = LocalDate.MIN;  //  Compiler error, because of `final`. 
        this.whenHired = whenHired;
    }
}
票数 295
EN

Stack Overflow用户

发布于 2009-02-02 05:18:29

有时候,显式地(为了可读性)变量不变是很好的。下面是一个简单的示例,使用final可以省去一些令人头疼的问题:

public void setTest(String test) {
    test = test;
}

如果你在setter中忘记了'this‘关键字,那么你想要设置的变量就不会被设置。但是,如果在参数上使用final关键字,则会在编译时捕获错误。

票数 237
EN

Stack Overflow用户

发布于 2009-02-01 09:54:46

是的,除了匿名类,可读性和意图声明,它几乎一文不值。然而,这三样东西是一文不值的吗?

就我个人而言,我倾向于不将final用于局部变量和参数,除非我在匿名内部类中使用该变量,但我肯定可以理解那些希望明确说明参数值本身不会改变的人的观点(即使它引用的对象改变了它的内容)。对于那些发现这增加了可读性的人来说,我认为这是一个完全合理的做法。

如果有人真的声称它确实以一种不是这样的方式保持数据不变,那么你的观点就更重要了--但我不记得看到过这样的说法。你是在暗示有大量的开发人员认为final的效果比它实际的效果更好吗?

编辑:我真的应该用Monty Python参考来总结所有这些;这个问题似乎有点类似于问“罗马人曾经为我们做过什么?”

票数 129
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/500508

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档