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[]
.
Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.
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.
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() { ... }
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,
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With