5

Here's a code example that doesn't compile:

public class Test {
    public static void main(String[] args) {
        method(1);
    }

    public static void method(int... x) {
        System.out.println("varargs");
    }

    public static void method(Integer... x) {
        System.out.println("single");
    }
}

Can someone tell me the reason why these methods are ambiguous ? Thank you in advance.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Dmytro
  • 1,850
  • 3
  • 14
  • 20
  • If I call `method(1, 2)` which one should it call? If it's the `int` method, how would I call the `Integer` method? – Boris the Spider Jan 10 '15 at 08:42
  • 1
    The same can be say about method(int) and method(Integer) but compiler can choose more preferable method, that's why i asked this question. – Dmytro Jan 10 '15 at 08:45
  • Np, it cannot. Because the JLS clearly states the _most specific_ method is called. So in that case `method(1)` would call the `int` method and `method((Integer) 1)` would call the `Integer` method. The point is that you have varargs. – Boris the Spider Jan 10 '15 at 08:46
  • But i still cannot understand the reason, because i can pass method(1,2,3) and method(new Ineger(1), new Inetger(2), new Inetger(3)) so i can call both methods. But i can't.... – Dmytro Jan 10 '15 at 08:50
  • In order to answer more specifically you will need to post your exact code that is causing the compiler error as an SSCCE and the full compiler error. The error is at method _invocation_ not _declaration_. – Boris the Spider Jan 10 '15 at 08:55

4 Answers4

7

There are 3 phases used in overload resolution (JLS 15.2.2):

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

  2. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.

  3. The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.

In your example, both methods are variable arity methods, so the third phase applies.

Now, since we have two methods to choose from, we look for the more specific method.

JLS 15.12.2.5. Choosing the Most Specific Method says :

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. 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, m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.

In your case you have two non-generic methods which are applicable by variable arity invocation (i.e. both have varargs). In order for one of the methods to be chosen when you call method(1), one of them has to be more specific than the other. In your case, each method only has one parameter, and for one of them to be more specific than the other, the type of that one parameter must be a subtype of the other method's parameter.

Since int is not a sub-type of Integer and Integer is not a sub-type of int, neither of your methods is more specific than the other. Hence the The method method(int[]) is ambiguous for the type Test error.

An example that would work :

public static void method(Object... x) {
    System.out.println("varargs");
}

public static void method(Integer... x) {
    System.out.println("single");
}

Since Integer is a sub-type of Object, the second method would be chosen when you call method(1).

Eran
  • 387,369
  • 54
  • 702
  • 768
6

Consider the method signatures

public static void foo(int a)

and

public static void foo(Integer a)

Before boxing and unboxing, the call foo(1) would not have been ambiguous. To ensure compatibility with earlier versions of Java, the call remains unambiguous. Therefore the first phase of overload resolution does not allow for boxing, unboxing, or variable arity invocation, which were all introduced at the same time. Variable arity invocation is when you call a varargs method by passing a sequence of parameters for the last argument (rather than an array).

However the resolution of method(1) for your method signatures allows for boxing and unboxing because both methods require a variable arity invocation. Since boxing is allowed, both signatures apply. Normally when two overloadings apply, the most specific overloading is chosen. However neither of your signatures is more specific than the other (because neither int nor Integer is a subtype of the other). Therefore the call method(1) is ambiguous.

You can make this compile by passing new int[]{1} instead.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • so if i understood boxing/unboxing is allowed for overloading with varargs while prohibited for overloading without it? – Dmytro Jan 10 '15 at 09:29
  • 1
    No, boxing/unboxing is allowed for both varargs and non-varargs. It's just that if you don't use varargs the compiler will choose a version not requiring boxing/unboxing in preference to one which does require boxing/unboxing. If varargs are involved compiler doesn't prefer the version not requiring boxing. – Paul Boddington Jan 10 '15 at 09:32
0

Because they are ambiguous. According to JLS you can either do widening, boxing or boxing-then-widening. In your example there are 2 methods parameters which can be boxed/unboxed to each other. On compile time though it's not visible because of varargs, which were always not absolutely clear in java.

Even Sun recommended developers not to overload varargs methods, there were bugs in compiler related to it (see here).

Community
  • 1
  • 1
davidluckystar
  • 928
  • 5
  • 15
  • That problem was fixed. "In your example there are 2 methods parameters which can be boxed/unboxed to each other" — but `method(int)` and `method(Integer)` can be boxed/unboxed to each other too but it's OK. – – Dmytro Jan 10 '15 at 09:25
-1

The difference between int and Integer is that Integer is an object type.you can use in situation like finding the maximum number of type int , or comparing to integers

Integer object is already associated with methods like compare method:

public static void method(int x, int y) {
    System.out.println(Integer.compare(x, y));
}

Find more at : http://docs.oracle.com/javase/7/docs/api/

JBALI
  • 89
  • 6