在Java中将抽象方法的参数声明为final

6
我有一个抽象类和一个抽象方法,我想让它们的参数是final的,也就是说,我不想允许实现抽象类和方法的重新给参数赋值。
编辑:这个动机并不是为了本身的不变性,而更多地与对象设计有关。 (实际上,在我的用例中,参数是一个集合,在实现抽象方法时将被改变。)相反,我希望向任何实现我的抽象类/方法的人传达这些变量不应该被重新分配的信息。 我知道我可以通过Java-doc来传达这一点,但我正在寻找更具约束力的东西-他们必须遵循的规则,而不仅仅是指导遵循。在非抽象方法中,我可以使用final关键字来实现这一点-例如:
public class MyClazz {
  public void doSomething(final int finalParameter){
    finalParameter++; // compile error - cannot assign a value to final variable
  }
}

然而,如果我在抽象方法中使用final关键字,那么它并不构成契约的一部分 - 也就是说,抽象方法的实现不需要 final 关键字,参数可以被重新赋值:
public abstract class MyAbstractClazz {
  public abstract void doSomething(final int finalVariable);
}

public class MyExtendedClazz extends MyAbstractClazz {
  @Override
  public void doSomething(int finalVariable) { // does not require final keyword
    finalVariable++; // so the variable is modifiable
  }
}

正如在回答这个SO问题中指出的那样,final关键字并不是方法签名的一部分,这就是为什么抽象类的实现不需要它的原因。
所以,有两个问题:
  1. 为什么final关键字不是方法签名的一部分?我知道它不是,但是我想知道是否有特殊的原因。

  2. 既然final关键字不是方法签名的一部分,是否有其他方法可以使抽象方法中的参数不可分配?

其他研究:
  • 这个SO问题涉及到了同样的问题,但没有回答我的两个问题。事实上,第二个问题被明确提出,但没有得到回答。

  • 许多与final关键字相关的问题/博客等都提到了“the final word”。然而,关于这个问题,相关的评论如下(虽然有用,但没有回答我的两个问题):

请注意,final参数不被视为方法签名的一部分,并且在解析方法调用时被编译器忽略。参数可以声明为final(或非final),而不会影响如何重写该方法。


但是,即使一个方法重新分配了传递给它的变量,对于调用者来说也没有任何影响,因为对象引用是按值传递的。 如果你通过修改对象的状态来改变对象,情况就会变得更加复杂。 - Matthias
你到底想要实现什么?不可变性? - Erik Pragt
@ErikPragt- 修改了有关动机的问题。 - amaidment
2个回答

12
我有一个抽象类和一个抽象方法,我希望该方法的参数是final - 也就是说,我不希望允许实现抽象类和方法的重新分配参数。
为什么不允许呢?这是一些实现细节。对于调用代码来说是不可观察到的,因此抽象方法没有理由指定它。这就是为什么它不是方法签名的一部分 - 就像synchronized一样。
方法应该实现其文档中所述的契约 - 但是如何选择实现契约取决于它本身。合同无法对参数的最终性说出任何有用的事情,因为Java始终使用传值调用。

Java总是使用按值传递。如果你接受对于对象来说,被传递的是指向对象的指针(你不需要进行任何魔法操作来使用它),那么这就是实际情况。 - Phil
@Phil:是的,它是一个引用。任何变量或类似表达式的值都是原始值或引用。它永远不是一个对象。 - Jon Skeet
@Jon Skeet:不,不是引用。是指针,按值传递。它们不是同一回事。http://javadude.com/articles/passbyvalue.htm - Phil
@Phil:将引用与指针进行比较是向C++程序员介绍它们的一件事,但当话题完全是Java时,你“纠正”我的术语使用以适应你对世界的首选观点而不是Java语言规范(以及Java开发人员)在各个地方使用的术语,我认为这是不合适的。 - Jon Skeet
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/44665/discussion-between-phil-and-jon-skeet - Phil
显示剩余8条评论

4

参数是按值传递的,如果你使用某个变量调用方法,则即使在方法内重新分配参数,该变量也不会被修改,这就是为什么 final 不应该成为合同的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接