I'm a little confused about Java's varargs methods:
public static int sum(int ...a) { return 0; } public static double sum(double ...a) { return 0.0; }
When I tried to invoke sum()
without passing any argument, then the int
version of method was invoked. I don't understand why; normally the compiler must raise an error.
By contrast, the following piece of code generates a compiler error when I try to invoke sum
without any argument:
public static int sum(int ...a) { return 0; } public static boolean sum(boolean ...a) { return true; }
The inclusion of generics gives rise to a new type of error that you must guard against ambiguity. Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict. Here is an example that involves method overloading.
Varargs are useful for any method that needs to deal with an indeterminate number of objects. One good example is String. format . The format string can accept any number of parameters, so you need a mechanism to pass in any number of objects.
Also, Varargs methods can be overloaded if required.
The general rule that applies here is this: if one method signature is strictly more specific than the other, then Java chooses it without an error.
Intuituively, a method signature is more specific if you could delete it entirely and the other, less specific one would be applicable to each existing invocation.
When presented with a choice between the signatures sum(int... args)
and sum(double... args)
, the signature sum(int... args)
is more specific because any invocation of that method could also be passed on to sum(double... args)
by applying a widening conversion. The same does not hold for a sum(boolean... args)
method, which cannot be similarly converted.
Java Language Specification, SE 8 version:
15.12. Method Invocation Expressions
15.12.2.5. Choosing the Most Specific Method
The Java programming language uses the rule that the most specific method is chosen.
...
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
...
- m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
...
A type S is more specific than a type T for any expression if S <: T (§4.10).
4.10. Subtyping
4.10.1. Subtyping among Primitive Types
double >1 float
float >1 long
long >1 int
As mentioned in this answer, there are rules followed when selecting which overloaded method to use.
To quote:
- Primitive widening uses the smallest method argument possible
- Wrapper type cannot be widened to another Wrapper type
- You can Box from int to Integer and widen to Object but no to Long
- Widening beats Boxing, Boxing beats Var-args.
- You can Box and then Widen (An int can become Object via Integer)
- You cannot Widen and then Box (An int cannot become Long)
- You cannot combine var-args, with both widening and boxing.
(Let's redefine rule 1 like so: "Primitive widening uses the most specific method argument as possible.")
So with these rules in mind we can get an idea of what's going on here:
According to rule number one, primitive widening uses the most specific method argument as possible. Since an int
is representing by a non-decimal number (e.g. 1
) and a double
is represented by a decimal-number with precision 32 bytes more than that of a float
(e.g. 1.0
), we can say that int
s are "less than" or "smaller than" double
s, and by that logic, int
s can be "promoted" to double
s and double
s can be "demoted" to int
s.
Put simply, a primitive that can be widened to another primitive (e.g. int
-> float
-> double
) is more specific than another. For example, an int
is more specific than a double
because 1
can be promoted to 1.0
.
When you passed in no arguments to these overloaded vararg methods of the same name, since the return is effectively the same (0 and 0.0 respectively), the compiler would choose to use the method that takes in a vararg of type int
since it is more specific.
So, then, when you introduced these same methods that take in int
s and boolean
s (types that cannot be widened to each other) respectively, the compiler now cannot choose a method to use since int
s cannot be "promoted" or "demoted" like int
s, float
s and double
s. Therefore, it will throw a compile error.
I hope this helps you to understand what's happening.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With