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

Community
  • 1
  • 1
Pawel
  • 1,457
  • 1
  • 11
  • 22
  • which one is being called? i guess B – Philipp Sander Dec 19 '13 at 22:32
  • 1
    probably worth removing the question since it already has an SO entry as well as a real bug report. – Mike 'Pomax' Kamermans Dec 19 '13 at 22:32
  • Yes, 7 is primitive, so B in output in second case. – Pawel Dec 19 '13 at 22:32
  • possible duplicate of [bug with varargs and overloading?](http://stackoverflow.com/questions/2521293/bug-with-varargs-and-overloading) – Steve P. Dec 19 '13 at 22:34
  • @Steve P. In version 7, the problem should be solved, there are 2 primitives. Here I have object and primitive. – Pawel Dec 19 '13 at 22:36
  • Very interesting, thanks for link to bug report too. I would disagree that the the examples are very similar though - the first one has two methods each of which has an Array as an argument. The two methods in the second have unique signatures, 1 with a Class, 1 with a primitive. – JoeG Dec 19 '13 at 22:37
  • still a problem in java 7 by the way – Philipp Sander Dec 19 '13 at 22:39
  • I wonder, whether this is a bug, or normal. Should be "B" in both cases? – Pawel Dec 19 '13 at 22:43
  • I think it is not a bug of Java. When compiler compile the line 'method(7);' the compiler get ambiguous and can't determine which 'method' will be called because Boxing & Unboxing feature. – LHA Dec 19 '13 at 22:44
  • passing array as paramater works – Pawel Dec 19 '13 at 22:47
  • Passing array works because compiler won't get ambiguous in this case. BTW. You should not design overload like this. – LHA Dec 19 '13 at 22:48
  • It is question similar to exemplary questions in OCPJP exam. – Pawel Dec 19 '13 at 23:02

3 Answers3

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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I understand it as: 7 is primitive so it should be converted to array - int[]. – Pawel Dec 19 '13 at 22:53
  • 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 Dec 19 '13 at 23:01
  • @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. – Stephen C Dec 19 '13 at 23:09
  • 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. – Pawel Dec 19 '13 at 23:17
  • 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. – Stephen C Dec 19 '13 at 23:36
  • As a side-note, many languages avoid this problem entirely by not supporting method/constructor overloading. For example, Python and Ceylon. In the case of Ceylon, this decision is clearly a reaction to the linguistic and usability problems that overloading can cause in Java. – Stephen C Dec 19 '13 at 23:40
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.

user987339
  • 10,519
  • 8
  • 40
  • 45
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.

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287