5

我有两个编译得很好的构造函数,但我希望 Java 会抱怨歧义的可能性。

public Foo(int id, Bar bar, String name, String description){
}

public Foo(int id, Bar bar, String... values){
}

是什么赋予了?

4

2 回答 2

7

Java 允许这些方法存在,因为它有关于如果两者都适用时将调用哪个方法的规则。具体来说,将选择固定参数方法(不带...)而不是可变参数方法(带...)。

JLS第15.12.2 节在确定选择哪种方法时声明如下:

第一阶段(第 15.12.2.2 节)执行重载决议,不允许装箱或拆箱转换,或使用变量 arity 方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。

这保证了在 Java SE 5.0 之前在 Java 编程语言中有效的任何调用都不会因为引入可变参数方法、隐式装箱和/或拆箱而被认为是模棱两可的。但是,变量 arity 方法的声明(第 8.4.1 节)可以更改为给定方法方法调用表达式选择的方法,因为变量 arity 方法在第一阶段被视为固定 arity 方法。例如,在已经声明 m(Object) 的类中声明 m(Object...) 会导致不再为某些调用表达式(例如 m(null))选择 m(Object),如 m(Object[] ) 更具体。

第二阶段(第 15.12.2.3 节)执行重载决议,同时允许装箱和拆箱,但仍排除使用变量 arity 方法调用。如果在此阶段没有找到适用的方法,则处理继续到第三阶段。

这确保了一个方法永远不会通过可变的方法调用来选择,如果它可以通过固定的方法调用来应用的话。

第三阶段(第 15.12.2.4 节)允许将重载与可变参数方法、装箱和拆箱相结合。

(强调我的)

示例代码:

class Bar{}

public class Foo{
   public static void main (String [] args){
      Foo main = new Foo(1, new Bar(), "name", "description");
      Foo main2 = new Foo(2, new Bar(), "name");
      Foo main3 = new Foo(3, new Bar(), "name", "description", "otherValues");
      Foo main4 = new Foo(4, new Bar());    
   }

   public Foo(int id, Bar bar, String name, String description) {
      System.out.println("name and description!");
   }

   public Foo(int id, Bar bar, String... values) {
      System.out.println("values!");
   }
}

这打印出来:

name and description!
values!
values!
values!

... 以表明 Java 将尽可能选择固定的 arity 方法。

于 2013-10-14T18:13:21.640 回答
0

我同意你的看法,肖恩,以下代码可以调用你定义的两个构造函数中的任何一个:

Foo foo = new Foo(3, new Bar(), "", "");

然而,当java人引入“变量参数表示法”时,他们决定上面将调用“最具体的构造函数”定义。在这种情况下,我有 2 个字符串参数,而您的第一个构造函数正好需要 2 个字符串参数,因此它将被调用。

仅当字符串参数多于或少于 2 个时才会调用第二个构造函数,例如:

Foo foo = new Foo(3, new Bar(), "", "", "");

甚至:

Foo foo = new Foo(3, new Bar());

我希望这有助于澄清为什么你没有让编译器抱怨它(这只是他们决定它应该工作的方式)。

于 2013-10-14T18:55:04.917 回答