为什么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必须是最后一个参数