23

The following code doesn't compile.

package varargspkg;

public class Main {

    public static void test(int... i) {
        for (int t = 0; t < i.length; t++) {
            System.out.println(i[t]);
        }

        System.out.println("int");
    }

    public static void test(float... f) {
        for (int t = 0; t < f.length; t++) {
            System.out.println(f[t]);
        }

        System.out.println("float");
    }

    public static void main(String[] args) {
        test(1, 2);  //Compilation error here quoted as follows.
    }
}

A compile-time error is issued.

reference to test is ambiguous, both method test(int...) in varargspkg.Main and method test(float...) in varargspkg.Main match

It seems to be obvious because the parameter values in the method call test(1, 2); can be promoted to int as well as float

If anyone or both of the parameters are suffixed by F or f, it compiles.


If we however, represent the receiving parameters in the method signature with respective wrapper types as follows

public static void test(Integer... i) {
    System.out.println("Integer" + Arrays.asList(i));
}

public static void test(Float... f) {
    System.out.println("Float" + Arrays.asList(f));
}

then the call to the method test(1, 2); doesn't issue any compilation error. The method to be invoked in this case is the one that accepts one Integer varargs parameter (the first one in the preceding snippet).

Why is in this case the error as in the first case not reported? It appears that auto-boxing and automatic type promotion are both applied here. Is auto-boxing applied first so that the error is resolved?

The Oracle docs says,

Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.

The last sentence in this link. It's however for the sake of better understanding varargs.

Also to add below code compiles just fine.

public class OverLoading {

    public static void main(String[] args) {
        load(1);
    }

    public static void load(int i) {
        System.out.println("int");
    }

    public static void load(float i) {
        System.out.println("float");
    }
}

EDIT:

The following is the snap shot that indicates the compilation error. I have created a new application therefore the package name is different.

enter image description here

I'm using JDK 6.

Tiny
  • 27,221
  • 105
  • 339
  • 599

4 Answers4

10

You can either Widen or Box but you cannot do both, unless you are boxing and widening to Object (An int to Integer(Boxing) and then Integer to Object(Widening) is legal, since every class is a subclass of Object, so it is possible for Integer to be passed to Object parameter)

Similarly an int to Number is also legal (int -> Integer -> Number) Since Number is the super class of Integer it is possible.

Let's see this in your example:

public static void test(Integer...i)

public static void test(Float...f)

There are some rules that are followed when selecting which overloaded method to select, when Boxing, Widening, and Var-args are combined:

  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 either widening or boxing

So, based on the above given rules:

When you pass two integers to above functions,

  • according to rule 3, it will have to be first Widened and then Boxed to fit into a Long, which is illegal according to rule 5 (You cannot Widen and then Box).
  • So, it is Boxed to store in Integer var-args.

But in first case, where you have methods with var-args of primitive types:

public static void test(int...i)
public static void test(float...f)

Then test(1, 2) can invoke both the methods (Since neither of them is more suitable for rule 1 to apply):

  • In first case it will be var-args
  • In second case, it will be Widening and then Var-args (which is allowed)

Now, when you have methods with exactly one int and one float:

public static void test(int i)
public static void test(float f)

Then on invoking using test(1), rule 1 is followed, and smallest possible widening (i.e. the int where no widening is needed at all) is chosen. So 1st method will be invoked.

For more information, you can refer to JLS - Method Invocation Conversion

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • @Tiny. I think you should have a look at what I have explained. I think it will be clear for you. :) – Rohit Jain Oct 14 '12 at 07:17
  • Rohit, to be precise, there's no such thing as *boxing an `int` to an instance of `Object`*. What happens, actually, when you pass an `int` to a function that takes an `Object` is that the `int` is autoboxed to an instance of `Integer`, which can be seen as an `Object` since every class inherits from Object. To verify this, `class x { boolean b(Object o) { return o.getClass() == Object.class;} void main(String... args) { System.out.println(b(1)); }}` will print false. Obviously, changing to `o.getClass() instanceof Object` will print true. – Bruno Reis Oct 14 '12 at 20:09
  • (and this is the reason why it is also legal to pass an `int` to a method that takes an instance of `Number` as a parameter -- `Integer` inherits from `Number`) – Bruno Reis Oct 14 '12 at 20:11
  • @BrunoReis Yeah I wrote that thing only in a direct way.. I should have written it clearly. :) – Rohit Jain Oct 14 '12 at 20:11
2

In Java, 1 is how you represent an int. It can be either auto-boxed to an instance of Integer or promoted to float, and this explains why the compiler can't decide on the method it should call. But it will never be auto-boxed to Long or Float (or any other type).

On the other hand, if you write 1F, it is the representation of a float, that can be auto-boxed to a Float (and, in the same spirit, will never be auto-boxed to an Integer or anything else).

Bruno Reis
  • 37,201
  • 11
  • 119
  • 156
2

In Java 6, the problem lies at the time of instantiation of your generics before finding which method is available to call.

When you write 1,2 
     -> it can be be both int[] or float[] and hence the issue being complained.

When you write 1,2F 
     -> it can be be only float[] and hence the NO issue being complained.

Same with other two option i.e.

When you write 1F,2 
     -> it can be be only float[] and hence the NO issue being complained.

When you write 1F,2F 
     -> it can be be only float[] and hence the NO issue being complained.

On the other hand, when you use int or float, there is no variable type instantiation. When you use 1, it tries to find method with int as argument first, if not, it promotes the type and identifies the method with float. If both methods available, one with the int will be used first.

Ambiguity issue will not come in Java 7 as it has better handling of data type check and promotion.

Community
  • 1
  • 1
Yogendra Singh
  • 33,927
  • 6
  • 63
  • 73
  • 1
    With your logic it should also give an error when we overwrite single parameter int and float. But its not giving any error. public class OverLoading { public static void main(String[] args) { load(1); } public static void load(int i){ System.out.println("int"); } public static void load(float i){ System.out.println("float"); } } – Subir Kumar Sao Oct 14 '12 at 06:53
  • When you override even the single parameter e.g. `1, 2F`, being one param as strict float, the only applicable method is `test(float..)` and not `main(int..)`. That how there is no ambiguity and hence no issue. – Yogendra Singh Oct 14 '12 at 06:58
  • So you say the output to example (now added to the question) will output float? – Subir Kumar Sao Oct 14 '12 at 07:00
  • Yes . If passed 1,2F, it will output float – Yogendra Singh Oct 14 '12 at 07:04
  • Surprise surprise it outputs int. Check yourself. – Subir Kumar Sao Oct 14 '12 at 07:04
  • I think we are on different tracks. I am talking about the last piece of code in the question class OverLoading. Lets take it on chat. – Subir Kumar Sao Oct 14 '12 at 07:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/17996/discussion-between-subirkumarsao-and-yogendra-singh) – Subir Kumar Sao Oct 14 '12 at 07:08
2

Why is in this case the error as in the first case not reported? It appears that auto-boxing and automatic type promotion are both applied here. Is auto-boxing applied first the error is resolved?

Just an opinion - in case of varargs the JVM actually has to create an array of arguments. In case of Integer and Float, it's obvious what type of array it should create. So, it probably might be the reason for no ambiguity error.

But still, it's kind of confusing, why it can't create an array of Integers, when by default 1, 3 are ints.

Looks like this has been discussed here in SO in the past bug with varargs and overloading? and is infact a bug, according to the discussion.

Community
  • 1
  • 1
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142