Varargs Java Ambiguous Call

JavaMethodsOverloadingVariadic Functions

Java Problem Overview


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;
}

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

As mentioned in this answer, there are rules followed when selecting which overloaded method to use.

To quote:

> 1. Primitive widening uses the smallest method argument possible > 2. Wrapper type cannot be widened to another Wrapper type > 3. You can Box from int to Integer and widen to Object but no to Long > 4. Widening beats Boxing, Boxing beats Var-args. > 5. You can Box and then Widen (An int can become Object via Integer) > 6. You cannot Widen and then Box (An int cannot become Long) > 7. 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 ints are "less than" or "smaller than" doubles, and by that logic, ints can be "promoted" to doubles and doubles can be "demoted" to ints.

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 ints and booleans (types that cannot be widened to each other) respectively, the compiler now cannot choose a method to use since ints cannot be "promoted" or "demoted" like ints, floats and doubles. Therefore, it will throw a compile error.

I hope this helps you to understand what's happening.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKarimSView Question on Stackoverflow
Solution 1 - JavaMarko TopolnikView Answer on Stackoverflow
Solution 2 - Javahmc_jakeView Answer on Stackoverflow