为什么varargs应该是方法签名中的最后一个?

如果我尝试编写如下方法

public void someStuff(Object ... args, String a ) 

我收到这个错误

someStuff方法的变量参数类型Object必须是最后一个参数。

我不完全理解变量参数类型的要求是最后一个。 任何输入都会有所帮助。

它遵循C惯例。 反过来,C约定基于CPU架构,它在堆栈上传递参数。 第一个非vararg参数以堆栈帧中的固定偏移量结束。 如果您可以先放置vararg参数,则以下参数的堆栈偏移量将取决于您将传递多少个vararg参数。 这将极大地复杂化访问它们所需的代码量。

在你的例子中,首先使用String a ,它在概念上在偏移0处独立于后面的vararg参数的数量。 但是使用String a最后一个,它可以在偏移args.size * 4等处 – 每次需要String a你必须计算args.size * 4

变量参数必须是最后一个,因此编译器可以确定哪个参数是哪个。

例如,假设你通过了

“测试”,“测试”,“测试”,“测试”

进入你的function

 public void someStuff(Object ... args, String a) 

如果您希望args变量包含3或4个字符串,Java无法解决问题。 在撰写本文时,你可能会很明显,但它含糊不清。

然而,当它是相反的方式

 public void someStuff(String a, Object ... args) 

Java编译器看到第一个字符串,将其粘贴到“a”中,然后知道剩余的字符串可以安全地放入args中,并且变量没有歧义。

因为这会使语言变得不必要地复杂化。 想象一下,如果你还允许其他语法:

 public void someStuff(String a, Object ... args, String b) { } 

甚至:

 public void someStuff(String a, Object ... args, int b, Object ... args2) { } 

第二种语法意味着一个字符串后跟任意数量的Object类型的参数,后跟一个整数,后跟更多的对象。 当然你可以设计一种可以接受这样的东西的语言,但是如果你还想指定args2必须包含至少一个元素,但是args可以为空呢? 我们为什么不能这样做呢? 你可以设计这样的语言。

归结起来,你想要规则有多复杂? 在这种情况下,他们选择了一个满足需求的简单选项。

那么一个String也是Object的一个实例,所以如果你使用varargs你的vararg数组必须是最后一个参数,因为编译器无法真正决定什么是args以及你的字符串是什么a。 将方法调用视为方法名称的元组和作为参数的对象列表。 如果你有两个这样的方法:

 public void someStuff(Object ... args, String a ) public void someStuff(String a, String b) 

编译器无法决定为someStuff选择什么方法(“Hello”,“Hello”)。 如果将String a作为第一个参数,则可以确定someStuff(String,String)比someStuff(String,Object)更具体。

给定如何使用var args的方法,任何其他格式都可能导致歧义。 使varargs持久化可防止可能的歧义,而无需额外的语法来解决会降低该特征的益处的模糊性

考虑以下方法声明:

 public void varargsAreCool(String surname, String firstname, String... nicknames) { // some cool varargs logic } 

当像varargsAreCool("John", "Smith") ,显然John Smith没有昵称。 像这样使用varargsAreCool("Andrew", "Jones", "The Drew", "Jonesy")

现在考虑以下无效方法声明:

 public void varargsAreCool(String surname, String... nicknames, String firstname) { // some cool varargs logic } 

varargsAreCool("John", "Smith") Smith John的昵称或姓氏? 如果是他的姓,我怎么表明他没有绰号? 要做到这一点,你可能不得不使用像这样的方法varargsAreCool("John", new String[]{}, "Smith") ,这种方法很笨拙,有点挫败了这个function的用途。

像这样使用varargsAreCool("Andrew", "The Drew", "Jonesy", "Jones")The Drew, Jonesy and Jones所有绰号和姓氏缺失? 同样,这种歧义可以解决,但代价是笨重的额外语法。

这是GeekOnJava文章中的Var-args的第五条规则:

要准备如上面程序所示的数组对象,如果我们要将var-args声明为第一个或第二个参数,编译器就不会知道数组对象中必须包含多少个参数。

并根据第五条规则:

即使它们是不同的类型,我们也不能声明多个var-arg参数。 因为,编译器不知道必须创建多少个值的var-arg数组对象。 它导致编译时错误。

 public void add(int... a,int...b){}//leads compile time error public void add(int... a,long...b){} //compile time error 

在上面的场景编译器说 –

方法add的变量参数类型int必须是最后一个参数