【问题标题】:Why doesn't Java allow omitting formal parameter names in a function declaration?为什么 Java 不允许在函数声明中省略形式参数名称?
【发布时间】:2011-03-14 11:42:56
【问题描述】:

我希望了解为什么Java 设计人员选择以这种方式实现函数声明。我经常听到有人说 Java 的设计者希望避免在其他语言(例如 C++)中做出糟糕的设计选择——通过类进行多重继承运算符重载请记住——为了使面向对象模型尽可能简单并鼓励良好的编程实践。在这种情况下是这样吗?此功能是否过于昂贵而无法相对实现它提供的(不可否认的边际)收益?

问题是,我看不到(而且我还在学习,所以这可能意味着蹲下!:D)允许在函数声明中省略形式参数名称的显着实现开销。我至少能想到一个这个特性不会受到伤害的例子:定义抽象函数。

无论如何,很高兴听到人们对 SO 的一些想法。顺便说一句,Java 语言规范的the relevant section (8.4.1) 解释了什么,但没有谈论为什么

编辑:添加代码 sn-p:

abstract void someFunc(int, int, int);

(我使用了一个抽象函数,因为这是我能想到的一个简单的例子)。

【问题讨论】:

  • Java 设计倾向于等待一个令人信服的理由去做某事(然后再考虑几年;)why not 论点不足以包含某物。通常还有其他方法可以做同样的事情(如果不是那么优雅),例如使用重载代替,这使得案例不那么引人注目。
  • 特别是 对于abstract 方法,我想说参数名称是必不可少的。不是为了 curse 的编译器(事实上默认 .class 文件甚至不包含方法参数名称),而是为了开发人员!如果不是通过名称,你怎么知道每个参数的作用?
  • @Joachim,是的,我明白你在说什么。 IMO,它很好地解释了为什么良好的编码实践必须需要使用正式名称,但它似乎并不能令人信服地解释为什么你会想要拥有这个功能完全的语言。例如,如果我正在编写简单、自包含的代码(我知道没有人会使用它),我不介意没有正式名称。彼得的回答对我来说更直观:没有足够令人信服的理由来拥有这个功能。
  • 两个 cmets:第一:语言的每一个添加特性倍增复杂性(不仅仅是增加它),因为它与所有其他特性交互(看看有多少角落案例泛型已经引入)。除非有 strong 案例 for 添加功能,否则应该忽略它。第二:即使没有其他人必须修改该代码,您也必须在几周/几个月内完成,而且您将很难阅读它。如果它真的是一次性代码,那么学习一些 Groovy 并在很短的时间内编写您的代码 ;-)

标签: java methods arguments declaration


【解决方案1】:

为什么应该允许这样做?

如果方法定义的参数没有名称,您将如何在代码中实际使用参数?

在 C 中情况略有不同:声明和实现是相互独立的(至少如果你“正确”地做的话)。在这里,将信息留在一个地方可以避免重复。

但在 Java 中没有单独的声明(除非您在接口中定义方法,在这种情况下没有实现,这意味着不再重复)。 只有实现。

【讨论】:

  • 是的,interfacesabstract functions 是我能想到的声明与实现分离的两种场景。
  • @neeraj:正如我所说:在这些情况下没有重复,因为那是你提到它的唯一点。这是必要的信息。
【解决方案2】:

您可以通过使用重载来删除形式参数,例如

class Three {
  public Three() {
    this(1);
  }

  public Three(int a) {
    this(a, 2);
  }

  public Three(int a, int b) {
    this(a, b, 3);
  }

  public Three(int a, int b, int c) {  }

  // can pass any number of `int` values.
  public void method1(int... varargs) {
  }

  public void method2(int a) {
     method2(a, 2);
  }

  public void method2(int a, int b) { }
}

编辑:来自我的评论。

Java 设计倾向于等待一个令人信服的理由来做某事(然后再考虑几年;)为什么不 论点不足以包含某些内容。通常还有其他方法可以做同样的事情(如果不是那么优雅),例如使用重载,这使得案例不那么引人注目

【讨论】:

  • 谢谢彼得。是的,我在另一个关于 SO 的问题上读到了这个。我有点希望更多地解释为什么语言是这样的。无论如何感谢您的回复。
  • @neeraj2608,我已经添加了我之前的评论,但基本上,当它可以通过其他方式实现时,没有必要添加这样的功能。
  • 接受您在帖子中的评论:A why not argument is not enough to include something. There are generally other ways of doing the same thing (if not as elegent) such as using overloading instead, which makes the case less than compelling
【解决方案3】:

不同的答案表明你并不清楚你在问什么 - 在问题中举个例子会有所帮助。

我想你会希望能够写作

public void method(int a, int, int) {
}

而不是

public void method(int a, int b, int c) {
}

只要bc 没有在方法中的任何地方使用(或者方法是抽象的,或类似的)。

那么,为什么不允许省略它们:

  • 这会使语法复杂化(不过并不多)
  • 您通常希望记录参数,为此您需要为它们命名。 (这也适用于接口和抽象/原生方法。)
  • 如果不使用参数,为什么还要使用它们?
  • 从理论上讲:形参声明是一种变量声明,不能没有名称的变量(数组元素除外)。
  • 它可能会使代码的读者感到困惑。

当我有不使用的参数时,我有时会给它们起名字ignoreddummy

【讨论】:

  • 嗯...信不信由你,我确实考虑过是否应该包含代码 sn-p 但我认为“在函数声明中省略形式参数名称”是不言自明的。 :-) 无论如何,这是我要谈论的第一个。我也会在问题中添加一个代码 sn-p。
【解决方案4】:

例如,我认为这只是使事物强类型化的同一哲学的一部分。您应该在编译时知道什么是方法以及如何使用它。

它是关于拥有一个接口,该接口始终采用您期望的参数并返回您期望的结果并抛出您期望的异常。如果我们有

class Adder {
    int add(int a, int b) {return a+b;}
    int add(int a, int b, int c) {return a+b+c;}
    int add(int a, int b, int c, int d) {return a+b+c+d;}
}

那么 Java 的设计者在想:如果你想“Adder.add”两个特定的数字,有一种方法可以做到。您永远不会使用三个或四个参数调用此方法,因为这对于该方法的设计用途毫无意义。如果你想让“Adder.add”的三个或四个数字,你应该分别制作这些方法。

现在也许这是一个糟糕的人为示例,但我的部分观点是同意这种语言功能并不总是有意义。

【讨论】:

    【解决方案5】:

    如果您谈论的是名称,而不是论据本身,原因很简单。在定义抽象方法时,您正在定义一个接口。为了使接口可用,处理它的人必须知道参数的含义。这就是名称的用途。

    当然,如果您将参数命名为 ab,您的代码也可以正常工作,但您没有这样做。你称他们为国家出生地。你用它来传达意义。

    Java 总是旨在迫使程序员尽可能地做正确的事情,并让自己很难自爆。这就是它被设计成这样的原因。

    【讨论】:

      【解决方案6】:

      这将是一个很棒的功能,因为您可以摆脱那个愚蠢的注释来忽略未使用的。

      假设我有这个:

      @FunctionalInterface
      interface ListFunc {
          Stream<Path> apply( Path dir ) throws IOException;
      }
      void doStuff( Path dir, ListFunc listFunc ) {
          List<Path> filenames = listFunc.apply( dir ).collect( Collectors.toList() );
          ...
      

      所以,简单的情况:

      doStuff( Paths.get( System.getProperty( "user.home" ) ), Files::list );
      

      但是单元测试呢?

      static Stream<Path> fixedListFunc( Path /* unused */ dir ) {
          List<Path> fixed = Lists.newArrayList( Paths.get( "first" ),
                  Paths.get( "second" ),
                  Paths.get( "third" ) );
          return fixed.stream();
      }
      
      @Test
      public void testDoStuff() {
          doStuff( null, fixedListFunc );
      

      如果能够省略未使用参数的名称,那就太好了。在这种情况下,不必重载或使用注释来忽略警告或其他内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-10-27
        • 2018-08-21
        • 2020-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多