Why varargs should be the last in a method signature?
Asked Answered
F

5

24

If I try to write a method like below

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

I get this error

The variable argument type Object of the method someStuff must be the last parameter.

I don't fully understand the requirement of variable argument type to be the last. Any inputs will be helpful.

Foliated answered 29/1, 2010 at 12:40 Comment(4)
Generally, the answer is "Because those are the rules". The rules are the rules. Why does it matter why the rules exist? What problem do you have?Murderous
@S.Lott: I agree, but I am still curious as to the rationale behind Sun's decision.Shoring
Whenever I see any error message I get this feeling that I am doing something wrong. What I am doing wrong here apart from breaking the rule ?Foliated
In this case, the answer happens to be "because C worked that way". Does that help? Or do you want details on the C compiler hack used to implement it in the early days of C?Murderous
K
24

It follows the C convention. The C convention in turn is based on CPU architectures which pass arguments on the stack. The first non-vararg arguments end up at a fixed offset in the stackframe. If you could put the vararg arguments first, the stack offset of the following arguments would depend on how many vararg parameters you would have passed. This would greatly complicate the amount of code needed to access them.

In your example, with String a first, it's conceptually at offset 0 independent how the number of vararg arguments that follow. But with String a last, it could be at offset 0, 4, 8, 12 etc - you'd have to calculate args.size * 4 everytime you needed String a.

Kreutzer answered 29/1, 2010 at 13:1 Comment(5)
Hmm..maybe this is what @Murderous was referring to.Foliated
It is, altough slightly simplified (hence the "conceptually at offset 0"). In practice you only have one stack which also holds register spills, return addresses, etc.Kreutzer
It is. The reasoning for ... being last in Java is "because that's the rule." The reason for ... being last in C is an insane piece of C-compiler trivia. The "why" isn't helpful. The rule just is.Murderous
It seems that linead's answer should be accepted as the correct answer.Rusch
@Murderous It isn't 'insane', and it certainly isn't 'trivia'. It's an entirely rational decision based on which of the two yields a better solution.Convolution
D
24

The variable argument has to be the last so the compiler can work out which argument is which.

For example, say you pass

"test", "test", "test", "test"

into your function

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

Java cannot work out if you want the args variable to contain 3 or 4 strings. It may be obvious to you at the time of writing but it's ambiguous.

However, when it's the other way around

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

The Java compiler sees the first string, stick it into "a" and then knows that the remaining strings can be safely put into args and there is no ambiguity over the variables.

Doherty answered 29/1, 2010 at 12:48 Comment(5)
Theoretically, it would be technically possible. The technical problem only starts when you use 3 or more arguments and varargs isn't the first or the last of them.Lissotrichous
@Balus: There is still no ambiguity even when the varargs is in the middle, only a difficulty of implementation in the compiler, but in theory it's possible. The real problem comes when you have more than one vararg term. Then you have to have things like backtracking to resolve which is meant. It basically becomes as difficult to resolve as regexp matching.Narcotize
would make for a fun language feature though: pass-by-regexp. void foo(^([0-9]*)(A|B.)(bar)?$)Kreutzer
It's not ambiguous. String a is not optional, you cannot skip it. So the fourth "test" must be a. There is no other possibility.Fiddler
Ambiguity could arise in many cases if method overloading is added to the equationAubrette
K
24

It follows the C convention. The C convention in turn is based on CPU architectures which pass arguments on the stack. The first non-vararg arguments end up at a fixed offset in the stackframe. If you could put the vararg arguments first, the stack offset of the following arguments would depend on how many vararg parameters you would have passed. This would greatly complicate the amount of code needed to access them.

In your example, with String a first, it's conceptually at offset 0 independent how the number of vararg arguments that follow. But with String a last, it could be at offset 0, 4, 8, 12 etc - you'd have to calculate args.size * 4 everytime you needed String a.

Kreutzer answered 29/1, 2010 at 13:1 Comment(5)
Hmm..maybe this is what @Murderous was referring to.Foliated
It is, altough slightly simplified (hence the "conceptually at offset 0"). In practice you only have one stack which also holds register spills, return addresses, etc.Kreutzer
It is. The reasoning for ... being last in Java is "because that's the rule." The reason for ... being last in C is an insane piece of C-compiler trivia. The "why" isn't helpful. The rule just is.Murderous
It seems that linead's answer should be accepted as the correct answer.Rusch
@Murderous It isn't 'insane', and it certainly isn't 'trivia'. It's an entirely rational decision based on which of the two yields a better solution.Convolution
N
9

Because that would make the language unnecessarily complex. Imagine if you also allowed other syntaxes:

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

Or even:

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

This second syntax means a string followed by any number of arguments of type Object, followed by an integer, followed by more objects. Sure you could design a language that could accept things like that, but what if you also wanted to specify that the args2 must contain at least one element, but args can be empty? Why can't we do that too? You could design such a language.

It boils down to, how complicated do you want the rules to be? In this case they chose a simple option that fulfils the needs.

Narcotize answered 29/1, 2010 at 12:48 Comment(0)
R
3

Well, a String is also an instance of Object, so if you are using varargs, your vararg array has to be the last parameter because the compiler can't really decide what is args and what is your string a. Think of the method call as a tuple of method name and a list of objects which are your parameters. If you have two methods like so:

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

The compiler couldn't decide what method to choose for someStuff("Hello", "Hello") . If you put your String a as the first argument it can decide that someStuff(String, String) is more specific than someStuff(String, Object).

Ridgway answered 29/1, 2010 at 12:51 Comment(2)
@Daff, I think your example has nothing to do with varargs. Even if the method is: public void someStuff(Object args, String a) the compiler still has to decide whether to use : someStuff(Object, String) or someStuff(String, String). Or is it I miss something in your explanation ?Interfaith
Hm ok I didn't know if I explained it the best way and I don't claim that this is THE correct explanation. Basically it is mainly a compatibility thing since varargs have been added later in the java language and are treated like an array. So my guess is that they didn't want to change the lookup algorithms for choosing the right method to call which you don't have to, when you can make sure, that only the last parameter can be a vararg.Ridgway
I
1

Given how a method with var args is used any other format could lead to ambiguities. Having the varargs last prevents possible ambiguities without requiring additional syntax to resolve the ambiguities which would lessen the benefit of the feature.

Consider the follow method declaration:

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

When used like varargsAreCool("John", "Smith") it is obvious that John Smith has no nicknames. When used like this varargsAreCool("Andrew", "Jones", "The Drew", "Jonesy").

Now consider the following invalid method declaration:

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

When used like varargsAreCool("John", "Smith") is Smith John's nickname or his surname? If it is his surname how do I indicate that he has no nicknames? To do this you would probably have to use the method like this varargsAreCool("John", new String[]{}, "Smith") which is clunky and somewhat defeats the purpose of the feature.

When used like this varargsAreCool("Andrew", "The Drew", "Jonesy", "Jones") are the The Drew, Jonesy and Jones all nicknames and the surname is missing? Again this ambiguity could be resolved but at the cost of clunky additional syntax.

Insecticide answered 29/1, 2010 at 13:6 Comment(1)
The ambiguities could be easily resolved by stating that all non vararg parameters must be present and are matched from left or from right in the parameter list, and the remaining 0 or more parameters make up the vararg. In your second example, it is indeed difficult to tell that John has no nickname, but then it is simply bad method parameter design: if one can anticipate that the parameterlist could be empty, one must not place it in the middle.Kulseth

© 2022 - 2024 — McMap. All rights reserved.