为什么我要使用关键字“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();
}