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

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

如何解决《为什么我要在Java中的方法参数上使用关键字"final"?》经验,为你挑选了7个好方法。

我不明白其中final的关键字是真的方便的时候它是在方法的参数使用.

如果我们排除使用匿名类,可读性和意图声明,那么对我来说似乎几乎一文不值.

强制某些数据保持不变并不像看起来那么强大.

如果参数是基元,那么它将没有任何效果,因为参数作为值传递给方法,并且更改它将在范围之外没有任何影响.

如果我们通过引用传递参数,那么引用本身就是一个局部变量,如果从方法中更改引用,那么从方法范围外部不会产生任何影响.

考虑下面的简单测试示例.尽管该方法改变了给定的参考值,但该测试仍然通过,但它没有效果.

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

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

public void nullify(Collection t) {
    t = null;
}

Jerry Sha.. 228

有时显而易见(为了可读性)变量不会改变.这是一个简单的例子,使用final可以节省一些可能的麻烦

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

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



1> Jerry Sha..:

有时显而易见(为了可读性)变量不会改变.这是一个简单的例子,使用final可以节省一些可能的麻烦

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

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


顺便说一句,你会看到警告"变量测试的分配无论如何"
@ b1naryatr0phy实际上它是一个编译器警告,而不仅仅是IDE-tip
@AvrDragon但是,我们也可能忽略这个警告.因此,总是有更好的东西可以阻止我们进一步发展,比如编译错误,我们将通过使用final关键字获得.
@AvrDragon这取决于开发环境.除非你想养成坏习惯,否则你不应该依赖IDE来为你捕捉这样的东西.
@SumitDesai"但是,我们也可能会忽略这个警告.所以,总是有更好的东西阻止我们走得更远,就像编译错误一样,我们将通过使用final关键字获得." 我接受你的观点,但这是一个非常强烈的声明,我认为许多Java开发人员会不同意.编译器警告是有原因的,有能力的开发人员不应该错误地"强迫"他们考虑其影响.

2> Basil Bourqu..:
停止变量的重新分配

虽然这些答案在理智上很有趣,但我还没有读到简短的简单答案:

如果希望编译器阻止将变量重新分配给其他对象,请使用关键字final.

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

让我们看看效果如何.

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

// 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.
}

将局部变量标记为final.这会导致编译器错误.

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",以便编译器可以查找并标记任何此类重新分配.

回想起来

正如其他答案所述......鉴于Java的原始设计目标是帮助程序员避免愚蠢的错误,例如读取数组的末尾,Java应该被设计为自动将所有参数/参数变量强制执行为"final".换句话说,参数不应该是变量.但事后看来是20/20的愿景,Java设计师当时已经满满的.

为完整性添加了另一个案例
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
 }


@StijndeWitt您的示例显示了重新分配参数变量的问题.你失去了信息**没有获得**作为回报:(a)你已经失去了传递的原始值,(b)你已经失去了调用方法的意图(调用者通过'Hello World!'或者我们做了什么默认).a和b都可用于测试,长代码以及稍后进一步更改值.我支持我的陈述:arg vars应该*永远不会被重新分配.你的代码应该是:`message =(msg ||'Hello World'')`.没有理由*不*使用单独的var.唯一的代价是几个字节的内存.
"作为良好的编程习惯(在任何语言中),你都不应该重新分配参数/参数变量[..]"对不起,我真的要打电话给你.重新分配参数是Javascript等语言的标准做法,其中传递的参数数量(或者即使有任何传递的参数)也不是由方法签名决定的.例如,给定一个签名:"函数说(msg)",人们将确保分配参数'msg',如下所示:"msg = msg ||'Hello World!';".世界上最好的Javascript程序员正在打破你的良好实践.只需阅读jQuery源代码即可.
使用`message =(msg ||'Hello World"')`后来我不小心使用`msg`冒险.当我想要的契约是"没有/ null/undefined arg的行为与传递`"Hello World"`"无法区分时,在函数的早期提交它是一个很好的编程习惯.[这可以通过以`if(!msg)开头返回myfunc("Hello World")来重新分配来实现;`但是多个参数变得难以处理.在极少数情况下,函数中的逻辑应该关心是否使用了默认值,我宁愿指定一个特殊的标记值(最好是公共值).
@Basil:它的代码(以字节为单位)和Javascript中的代码更多.重.与许多事情一样,这是基于意见的.完全可以完全忽略这种编程习惯,仍然可以编写优秀的代码.一个人的编程实践并不能成为每个人的实践.你可以随心所欲地坚持下去,无论如何我选择不同的方式.这会让我成为一个糟糕的程序员,还是我的代码坏代码?
@ BeniCherniavsky-Paskin你描述的风险只是因为`message`和`msg`之间的相似性.但是,如果他称之为`processedMsg`或其他提供额外背景的东西 - 错误的可能性要低得多.专注于他所说的*不是*他说的"怎么样".;)
@StijndeWitt关于这个主题的一篇有趣的文章:http://lpar.ath0.com/2008/08/26/java-annoyance-final-parameters/
“从不放任自流”。嗯,不,在编程中不要说“从不”,只能说“从不说”。有很多合法的用例来分配给一个参数。预设,预处理,解码等。我希望程序员不要再说“从不”。他们这样做时几乎总是不正确的。
@LawrenceDol在理论层面,我讨厌说"从不",因为总会有例外,禁止的方法最适合这种情况.但是,在实际层面上,我真的很喜欢它,因为它强制执行规则并减少许多程序员在共享代码库上进行协作时需要做出的决策量,它使整体设计保持一致,只需适量(最小量)例外.因此,可能更准确地说:"如果A不能解决问题,总是更喜欢A而且只做B".
@ user949300在你的函数之外没有人看到变化......直到他们必须调试它.添加`final`意味着你可以做的少一件事(错误的).

3> Jon Skeet..:

是的,排除匿名类,可读性和意图声明它几乎一文不值.这三件事虽然毫无价值吗?

我个人倾向于不使用final局部变量和参数,除非我在匿名内部类中使用变量,但我当然可以看到那些想要明确参数值本身不会改变的人的观点(甚至如果它引用的对象改变其内容).对于那些发现增加可读性的人,我认为这是完全合理的事情.

如果有人真的声称它确实以不支持的方式保持数据不变,那么你的观点将更为重要- 但我不记得看到任何此类声明.您是否建议有大量开发人员认为其final效果比实际更有效?

编辑:我应该用Monty Python参考总结所有这些; 这个问题看起来有点类似于问"罗马人为我们做过什么?"


那么......罗马人为我们做过什么?
但用他的丹麦语来解释Krusty,他们为我们做了什么*LATELY*?=)

4> Michael Borg..:

让我解释一下你必须使用final 的一个案例,Jon已经提到过:

如果在方法中创建匿名内部类并在该类中使用局部变量(例如方法参数),则编译器会强制您使参数最终:

public Iterator createIntegerIterator(final int from, final int to)
{
    return new Iterator(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

这里fromto参数需要是最终的,因此它们可以在匿名类中使用.

这个要求的原因是:局部变量存在于堆栈中,因此它们仅在执行方法时存在.但是,从该方法返回匿名类实例,因此它可能会存活更长时间.您无法保留堆栈,因为后续方法调用需要它.

所以Java所做的就是将这些局部变量的副本作为隐藏实例变量放入匿名类中(如果检查字节代码,可以看到它们).但是如果它们不是最终的,那么人们可能会期望匿名类和方法看到另一个对变量做出的改变.为了保持只有一个变量而不是两个副本的错觉,它必须是最终的.



5> Fortyrunner..:

我一直在使用final参数.

它增加了那么多吗?并不是的.

我会把它关掉吗?没有.

原因是:我发现3个错误,其中人们编写了草率代码并且无法在访问器中设置成员变量.事实证明,所有的漏洞都很难找到.

我希望看到这在未来的Java版本中成为默认值.价值/参考物的传递使很多初级程序员绊倒了.

还有一件事......我的方法往往具有较少的参数,因此方法声明中的额外文本不是问题.


我也打算这样做,最后是未来版本的默认版本,你必须指定构思的"可变"或更好的关键字.这是一篇很好的文章:http://lpar.ath0.com/2008/08/26/java-annoyance-final-parameters/

6> Germán..:

在方法参数中使用final与调用者方面的参数发生的事情无关.它只是意味着它在该方法内部没有变化.当我尝试采用更具功能性的编程风格时,我看到了它的价值.


确切地说,它不是功能接口的一部分,只是实现.令人困惑的是java [允许(但是dirsregards)](/sf/ask/17360801/)`final`对接口/抽象方法中的参数声明.

7> starblue..:

我个人不会在方法参数上使用final,因为它会给参数列表增加太多的混乱.我更喜欢强制执行方法参数不会像Checkstyle这样改变.

对于局部变量,我尽可能使用final,我甚至让Eclipse在我的个人项目设置中自动执行.

我当然喜欢像C/C++ const这样强大的东西.

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