5

This will not compile:

public class Methods
{

    public static void method(Integer... i)
    {
        System.out.print("A");
    }

    public static void method(int... i)
    {
        System.out.print("B");
    }

    public static void main(String args[])
    {
        method(7);
    }
}

This will compile and work:

public class Methods
{

    public static void method(Integer i)
    {
        System.out.print("A");
    }

    public static void method(int i)
    {
        System.out.print("B");
    }

    public static void main(String args[])
    {
        method(7);
    }
}

First and second example are very similar. First uses varargs, second not. Why one works, second not. 7 is primitive, so second method should be called in both cases. Is it normal behaviour?

I found it: Bug report Stack overflow

12
  • which one is being called? i guess B Commented Dec 19, 2013 at 22:32
  • 1
    probably worth removing the question since it already has an SO entry as well as a real bug report. Commented Dec 19, 2013 at 22:32
  • Yes, 7 is primitive, so B in output in second case. Commented Dec 19, 2013 at 22:32
  • possible duplicate of bug with varargs and overloading? Commented Dec 19, 2013 at 22:34
  • @Steve P. In version 7, the problem should be solved, there are 2 primitives. Here I have object and primitive. Commented Dec 19, 2013 at 22:36

3 Answers 3

4

This is a high-level informal summary of what is going on.

Firstly varargs syntax is really just syntactic sugaring for passing an array. So method(7) is actually going to pass an array of ... something.

But an array of what? There are two options here corresponding to the two overloads of the method; i.e an int[] or a Integer[].

If there are two or more overloads that could work (i.e. right method names, right numbers of arguments, convertible values) then the resolution process will chose the overload that is an exact match over a match that requires conversions, and complain if the only candidates require conversions. (This is a drastic simplification of the rules ... see the JLS section 15.12 for the complete story ... and be prepared for a long / difficult read!)

So what is happening in your first example is that it is trying to decide between two methods that both require conversions; i.e. int to int[] versus int to Integer[]. Basically it cannot decide which alternative to use. Hence a compilation error that says that the call is ambiguous.

If you change the varargs call to a call passing an explicit Integer[] or int[], you now get an exact match to one of the two overloads ... and the rules above say that this is not ambiguous.


I understand it as: 7 is primitive so it should be converted to array - int[].

The problem is that 7 can also be converted to an Integer[] ... by auto-boxing the int first.

Sign up to request clarification or add additional context in comments.

6 Comments

I understand it as: 7 is primitive so it should be converted to array - int[].
We can distinguish both. It should be: 7 - converted to int[], Integer(7) - converted to Integer[]. Second example works and it is able to distinguish.
@Pawel - That's not the way the language is specified. The JLS says that int can be converted to Integer[] in the context of a varargs call. The fact that you (the programmer) can distinguish the cases and (intuitively) say which conversion is "right" does not help the compiler. The compiler is bound by the rules, and the rules say this is ambiguous.
I know, but the rule is a bit strange, isnt't it? But I understand the rule and what's is going on. It works for array, it doesn't work for one argument.
Now you are getting into a subjective topic ... whether the rule is "strange" or not. Bear in mind that both auto-boxing and varags were retro-fitted into an earlier design for Java. The rules for method overload resolution in Java 5 and later had to be consistent with what Java 4 and earlier did ... when those constructs weren't part of the language.
|
2

Multiple arguments must be passed in an array, but the varargs hides the process. In the above varargs method, parameter acts as an int array with a reference name. So if you change it to:

    public static void main(String args[])
    {
        int[] s = {7};
        method(s);
    }

first class will compile and work properly.

Comments

0

The answer is rather difficult. But it is described in the JLS §15.12. Method Invocation Expressions. In these specifications, "variable arity" methods are methods with variable number of arguments, so varargs.

§15.12.2.4. Phase 3: Identify Applicable Variable Arity Methods

The method m is an applicable variable-arity method if and only if all of the following conditions hold:

  • For 1 ≤ i < n, the type of ei, Ai, can be converted by method invocation conversion to Si.

  • If k ≥ n, then for n ≤ i ≤ k, the type of ei, Ai, can be converted by method invocation conversion to the component type of Sn.

  • If k != n, or if k = n and An cannot be converted by method invocation conversion to Sn[], then the type which is the erasure (§4.6) of Sn is accessible at the point of invocation.

  • If m is a generic method as described above, then Ul <: Bl[R1=U1...,Rp=Up] (1 ≤ l ≤ p).

If no applicable variable arity method is found, a compile-time error occurs.

Otherwise, the most specific method (§15.12.2.5) is chosen among the applicable variable-arity methods.

So, we should look further to §15.12.2.5.

§15.12.2.5. Choosing the Most Specific Method

One variable arity member method named m is more specific than another variable arity member method of the same name if either:

  1. One member method has n parameters and the other has k parameters, where n ≥ k, and:

    • The types of the parameters of the first member method are T1, ..., Tn-1, Tn[].

    • The types of the parameters of the other method are U1, ..., Uk-1, Uk[].

    • If the second method is generic then let R1 ... Rp (p ≥ 1) be its type parameters, let Bl be the declared bound of Rl (1 ≤ l ≤ p), let A1 ... Ap be the type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui (1 ≤ i ≤ k-1) and Ti << Uk (k ≤ i ≤ n), and let Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ k).

    • Otherwise, let Si = Ui (1 ≤ i ≤ k).

    • For all j from 1 to k-1, Tj <: Sj, and,

    • For all j from k to n, Tj <: Sk, and,

    • If the second method is a generic method as described above, then
      Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p).

(T <: S means T is a subtype of S)

Your methods do not match these conditions, so there is no "Most Specific" method. So, it says a little bit further:

It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:

  • If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:

    • If exactly one of the maximally specific methods is not declared abstract, it is the most specific method.

    • Otherwise, if all the maximally specific methods are declared abstract, and the signatures of all of the maximally specific methods have the same erasure (§4.6), then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type.

      However, the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods.

  • Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.

So, the conclusion: your methods are ambiguous following the JLS.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.