为什么我要使用关键字“quot;“最后的”;关于Java中的方法参数?

为什么我要使用关键字“quot;“最后的”;关于Java中的方法参数?,java,pass-by-reference,final,pass-by-value,Java,Pass By Reference,Final,Pass By Value,我不明白当final关键字用于方法参数时,它在哪里真的很方便 如果我们排除匿名类、可读性和意图声明的使用,那么对我来说几乎毫无价值 强制某些数据保持不变并不像看上去的那么强烈 如果该参数是一个原语,那么它将不起作用,因为该参数作为值传递给该方法,并且在范围之外更改它将不会产生任何影响 如果我们通过引用传递一个参数,那么引用本身就是一个局部变量,如果引用是从方法内部更改的,那么从方法范围之外不会有任何影响 考虑下面的简单测试示例。 该测试通过,尽管该方法更改了给定给它的参考值,但没有效果 pu

我不明白当
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;
}
public void testNullify(){
集合c=新的ArrayList();
取消(c);
资产净值(c);
最终收集c1=c;
资产真(c1等于(c));
改变(c);
资产真(c1等于(c));
}
私人作废更改(集合c){
c=新的ArrayList();
}
公共无效作废(集合t){
t=零;
}

是的,除去匿名类、可读性和意图声明,它几乎一文不值。但这三样东西都一文不值吗

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


如果有人真的声称它确实以一种不一样的方式保持了数据的恒定性,那么你的观点将更加重要——但我不记得看到过任何这样的说法。你是说有相当多的开发人员认为
final
比实际效果更好吗


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

在方法参数中使用final与调用方的参数发生了什么无关。它只意味着将其标记为在该方法内没有更改。当我尝试采用更具功能性的编程风格时,我看到了其中的价值。

我个人不使用final on方法参数,因为它给参数列表添加了太多的混乱。 我更喜欢强制方法参数不通过像Checkstyle这样的东西更改

对于局部变量,只要可能,我就使用final,我甚至让Eclipse在我的个人项目设置中自动完成这项工作


我当然想要像C/C++const这样更强的东西。

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

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

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}
public迭代器createIntegerIterator(final int from,final int to)
{
返回新的迭代器(){
int索引=从;
公共整数next()
{
返回索引++;
}
公共布尔hasNext()
{

返回指数我在参数上一直使用final

有那么多吗?没有

我能把它关掉吗?不

原因是:我发现了3个错误,人们编写了草率的代码,并且未能在访问器中设置成员变量。所有的错误都很难找到

我希望看到这在未来的Java版本中成为默认值。传递值/引用的事情让很多初级程序员感到困惑


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

有时,明确(为了可读性)变量不改变是很好的。下面是一个简单的示例,其中使用
final
可以避免一些可能的麻烦:

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

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

将final添加到参数声明的另一个原因是,它有助于识别需要重命名为final的变量“提取方法”重构的一部分。我发现,在开始大型方法重构之前,向每个参数添加final可以很快告诉我在继续之前是否需要解决任何问题

但是,我通常在重构结束时将它们作为多余的删除。

停止变量的重新分配 虽然这些答案在智力上很有趣,但我没有读过简单的简短答案:

如果希望编译器阻止错误,请使用关键字final 将变量重新指定给不同的对象

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

例子 让我们看看效果如何

考虑这个简单的方法,其中两个变量(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.
}
将局部变量标记为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";  
}
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。这也会导致编译器错误

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

永远不要重新分配参数 作为良好的编程实践(在任何语言中),您都不应该重新分配参数/参数
public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}