【问题标题】:Is it problematic to assign a new value to a method parameter?为方法参数分配新值是否有问题?
【发布时间】:2011-01-14 19:44:52
【问题描述】:

Eclipse 有一个选项可以在分配给方法的参数(在方法内部)时发出警告,如下所示:

public void doFoo(int a){
   if (a<0){
      a=0; // this will generate a warning
   }
   // do stuff
}

通常我会尝试激活(并注意)几乎所有可用的编译器警告,但在这种情况下我不确定是否值得。

我看到了在方法中更改参数的合法案例(例如:允许参数“未设置”(例如 null)并自动替换默认值),但很少会导致问题,除非它可能在方法中间重新分配参数会有点混乱。

您是否使用此类警告?为什么/为什么不?

注意:

避免这个警告当然等同于使方法参数final(只有这样它是一个编译器错误:-))。所以这个问题Why should I use the keyword "final" on a method parameter in Java? 可能是相关的。

【问题讨论】:

标签: java eclipse compiler-warnings


【解决方案1】:

对我来说,只要你早点清楚,就可以了。正如您所说,将其深埋在 30 行函数中的四个条件中并不理想。

显然,在对对象引用执行此操作时,您也必须小心,因为在给定对象上调用方法可能会更改其状态并将信息传回给调用者,但当然,如果您使用自己的占位符进行替换,则不会传达该信息。

另一方面是,声明一个新变量并为其分配参数(如果参数需要默认,则为默认值)可能会更清晰,而且几乎肯定不会降低效率——任何体面的编译器(无论是主编译器)或 JIT)将在可行时对其进行优化。

【讨论】:

  • 我不清楚重新分配对象引用如何使对象(您不再具有引用)将信息传回给调用者。你能给我解释一下吗?
  • @OscarMk:我的意思就是:不会。 :-) 所以(比如说)你收到了参数obj,如果存在某个条件,你会在函数顶部附近为其分配一个新的引用。在您调用obj.foo() 的函数底部附近。如果您分配了对obj 的新引用,那么显然调用者的对象将不会收到foo 调用,您的替换将 - 但看最后的代码并不明显会发生这种情况。
【解决方案2】:

不同的编译器警告可能适用于不同的情况。当然,有些适用于大多数或所有情况,但这似乎不是其中之一。

我认为这个特殊的警告是编译器给你的选项,让你在需要时警告方法参数被重新分配,而不是方法参数不应该被重新分配的规则。您的示例构成了一个完全有效的案例。

【讨论】:

    【解决方案3】:

    令人困惑的部分是警告的原因。如果您在方法中为参数重新分配新值(可能是有条件的),那么不清楚 a 是什么。这就是为什么保持方法参数不变被认为是好的风格。

    【讨论】:

    • 我不同意 - 通常需要对输入进行清理。
    • 将其清理为新的局部变量。
    • @Mnementh,如果您为此目的创建一个新的局部变量,那么为所有类似变量选择适当的名称可能会很棘手。
    • @finnw:这实际上是我经常“打破”规则的最大原因。我只是不够有创造力。 :-)
    • @finnw/Crowder: ...由于我缺乏想象力,我个人非常喜欢语义/语用匈牙利语前缀表示法,使我们能够为对象成员变量使用相同的名称(例如“m_”疣)、方法参数和局部变量(例如“the...”)。并不是说我已经设法保持一致!
    【解决方案4】:

    分配方法参数并不是大多数人期望在大多数方法中发生的事情。由于我们阅读代码时假设参数值是固定的,因此分配通常被认为是不好的做法,如果只是按照惯例和 principle of least astonishment

    分配方法参数总是有其他选择:通常本地临时副本就可以了。但通常,如果您发现需要通过重新分配参数来控制函数的逻辑,则可以从重构为更小的方法中受益。

    【讨论】:

    • “分配一个方法参数并不是大多数人期望发生的事情......”我会反对这是一个普遍的说法。事实上,我会说在 函数式 语言和类似语言(包括 JavaScript)中,它已经接近常态了。根据我的经验,它在 Java 和 C++ 中不太常见,在 C 中既不常见也不罕见。我不知道为什么会这样,但这可能与人们在不同语言和环境中解决问题的方法有关.
    • 吹毛求疵:在函数式语言中,不会发生赋值,只会发生新的绑定,就像命令式语言声明一个新的临时变量一样。实际上,我建议您所描述的 是 JavaScript 声誉不佳的原因之一,尽管它作为基于原型的 OO 语言的设计实际上非常漂亮。曾经有人说 Amiga 比它的粉丝要好得多......
    • 问题被标记为 Java,而不是 Javascript 或函数式语言。对于通常的命令式语言,就像这里写的那样。
    【解决方案5】:

    我有时会在以下情况下使用它:

    void countdown(int n)
    {
       for (; n > 0; n--) {
          // do something
       }
    }
    

    避免在for循环中引入变量i。通常我只在很短的函数中使用这些“技巧”。

    我个人非常不喜欢以这种方式在函数内“纠正”参数。我更喜欢通过断言来捕捉这些并确保合同是正确的。

    【讨论】:

    • 对不起,但我认为你的例子是完美的情况 for 这个警告。我觉得它不必要地复杂。例如,如果函数稍后增长(函数总是这样做),并且您尝试使用 n 来打印“Iteration out of ”怎么办?
    【解决方案6】:

    我通常不需要为方法参数分配新值。

    至于最佳实践 - 警告也避免了面对以下代码时的混淆:

           public void foo() {
               int a = 1;
               bar(a);
               System.out.println(a);
           }
    
           public void bar(int a) {
               a++;
           }
    

    【讨论】:

      【解决方案7】:

      如果参数是引用类型,重新分配给方法参数变量通常是错误的。

      考虑以下代码:

      MyObject myObject = new myObject();
      myObject.Foo = "foo";
      doFoo(myObject);
      
      // what's the value of myObject.Foo here?
      
      public void doFoo(MyObject myFoo){   
         myFoo = new MyObject("Bar");
      }
      

      许多人会期望在调用 doFoo 之后,myObject.Foo 将等于“Bar”。当然不会——因为Java不是通过引用传递,而是通过引用值传递——也就是说,一个拷贝的引用传递给方法。重新分配给该副本仅在本地范围内有效,而在调用点无效。这是最常被误解的概念之一。

      【讨论】:

      • 实际上,Java 方法只是按值调用。不过,我认为这与问题没有太大关系。
      • @dan 我认为这是相关的。 *为方法参数分配新值是否有问题?” - 是的,可以,因为它可能不会像人们期望的那样。
      • 是的,但在这种特殊情况下,您指的是不了解该语言如何工作的人。编译器警告不会防止这种情况发生。
      【解决方案8】:

      你应该编写没有副作用的代码:每个方法都应该是一个不会改变的函数。否则它是一个命令,它可能很危险。

      在 DDD 网站上查看 commandfunction 的定义:

      功能: 一种计算并返回结果而没有可观察到的副作用的操作。

      Command :对系统产生某些改变的操作(例如 例如,设置一个变量)。一个 故意创建的操作 副作用。

      【讨论】:

      • @Jean-Philippe:(我不考虑修改方法参数的好习惯)问题不在于副作用。实际上,将 Java 中的原始参数更改为完全零副作用,可以看出 Java 是“按值传递”。您正在介绍引用透明性和纯函数的概念,这在函数式编程世界中可能很有趣。但是这里我们讨论的是 Java,你会发现在 Java 中,很多的 Java 方法实际上并不是引用透明的。最初的问题不是关于“设置变量”,而是关于修改方法参数。
      猜你喜欢
      • 1970-01-01
      • 2019-09-16
      • 2020-05-06
      • 1970-01-01
      • 2019-12-26
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多