Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading varargs arrays, choosing method

I have a two overloaded methods: foo and bar

//Object[]... vs Integer[]...
public static String foo(Object[]... args)  { return "Object[] args"; }
public static String foo(Integer[]... args) { return "Integer[] args";}

//Object... vs Integer[]...
public static String bar(Object... args) {return "Object args";}
public static String bar(Integer[]... args) {return "Integer[] args";}

Now when I use them like:

Integer[] i = { 5 };
System.out.println(foo(i));//Object[]... vs Integer[]...
System.out.println(bar(i));//Object... vs Integer[]...

I am getting

Integer[] args
Object args

Here is the question: why do we have 2 different outputs?
Integer[] can be implicitly cast to both Object, and Object[].

like image 228
manvel Avatar asked Nov 01 '15 20:11

manvel


People also ask

Can we overload varargs in Java?

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

How do the overloading methods can be ambiguous?

Sometimes unexpected errors can result when overloading a method that takes a variable length argument. These errors involve ambiguity because both the methods are valid candidates for invocation. The compiler cannot decide onto which method to bind the method call.

What is method overloading in Java with example?

In Java, two or more methods may have the same name if they differ in parameters (different number of parameters, different types of parameters, or both). These methods are called overloaded methods and this feature is called method overloading. For example: void func() { ... }


2 Answers

This is basically compiler deciding to call the most specific method among all.

When you call

System.out.println(foo(i));//Object[]... vs Integer[]...

it will call the foo(Integer[]... args)

Because at run time the JVM delegates the call to the method with Integer[][] argument and not method with Object[][] param as specified by varags. As it will be more specific to call method with Integer[][] rather than Object[][].


In the later statement, when you call

System.out.println(bar(i));//Object... vs Integer[]...

it will go to the bar(Object... args)

Again by using varags, the type of param will be Object[] and not Object[][]. Again the compiler will call the most specific method which will be the one having Object... args.

If you change the method signature by removing varags as per following:

   //Object... vs Integer[]...
    public static String bar(Object args) {

        return "Object args";
    }

    public static String bar(Integer[] args) {
        return "Integer[] args";
    }

then you will notice that it will call the bar(Integer[] args) as it is more specific to the method call.

So to be more precise as per JLS Subtyping among Array Types,

  • If S and T are both reference types, then S[] > T[] iff S > T.
  • Object > Object[]

This means that a call of Integer[] will be made to method having Integer[][] and not Object[][]. Where as a call of Integer[] will be made to Object[] rather than Integer[][].

See here for choosing the most specific method.

like image 197
Pavitra Kansara Avatar answered Oct 20 '22 05:10

Pavitra Kansara


In the first case, the type of args is actually Integer[][], i.e., your array was boxed up into another array by varargs. The compiler chooses the Integer[] version because it is the most specific type.

In the second case, args == i and is an Integer[]. In this case, the compiler had to choose between wrapping it up in a new array to call the Integer[]... version or just casting your Integer[] to an Object[]. It chose the second one because that's the rule.

The moral of the story is: don't overload varargs methods -- it's confusing.

like image 3
Matt Timmermans Avatar answered Oct 20 '22 04:10

Matt Timmermans