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

时间:2010-01-29 12:40:39

标签: java method-signature

如果我尝试编写如下方法

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

我收到此错误

  

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

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

6 个答案:

答案 0 :(得分:21)

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

例如,假设您通过了

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

进入你的功能

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

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

然而,当它是相反的时候

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

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

答案 1 :(得分:21)

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

在您的示例中,首先使用String a,它在概念上在偏移0处独立于后面的vararg参数的数量。但最后String a可能会偏移0,4,8,12等 - 每次需要args.size * 4时,您必须计算String a

答案 2 :(得分:9)

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

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

甚至:

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

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

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

答案 3 :(得分:3)

一个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)更具体。

答案 4 :(得分:0)

考虑如何使用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")这样的方法,这种方法很笨拙,并且在某种程度上违背了该功能的目的。

如果像varargsAreCool("Andrew", "The Drew", "Jonesy", "Jones")那样使用The Drew, Jonesy and Jones所有昵称且姓氏丢失了吗?同样,这种模糊性可以解决,但代价是笨重的额外语法。

答案 5 :(得分:0)

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

  

如上所述,准备数组对象,如果我们愿意的话   将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必须是最后一个   参数