So I have the following overloaded methods:
private static void foo(short... a)
{
System.out.println("Calling var-len");
}
private static void foo(int a, int b)
{
System.out.println("Calling int-int");
}
private static void foo(int a, double b) //(3)
{
System.out.println("Calling int-double");
}
private static void main (String[] args)
{
foo((short)2, (short)5); //This one outputs "Calling int-int"
}
I know that variable arity method has the lowest priority during method resolution phases, so in this case if I call foo((short)2, (short)4); I would get "Calling int-int".
HOWEVER, if I change method (3) to foo(short a, double b), the variable arity method is picked! (Java 7).
Could anyone explain this?
According to the specification:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2
"However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase."
Your question is a typical example for numerical promotion in java. It tries to resolve for the exact signature match (short, short), but in your case you don't have that method. Java checks for subsequent compatible data type (say int), it promotes both short to int data types and tries to find a match, it finds it.
If none of the numeric promotion works, it will resolve to variable argument method in your code.
Please read on numeric promotions on below link..
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html
Related
I have method calling with passing parameters as (4,5).
I have two methods with parameters :
method1(int a, int b);
method1(Integer a, Integer b);
which method will be called and why?
This has been specified in The Java® Language Specification, §15.12.2. Compile-Time Step 2: Determine Method Signature:
…
The remainder of the process is split into three phases, to ensure compatibility with versions of the Java programming language prior to Java SE 5.0. The phases are:
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.
…
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.
…
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
Therefore, since method1(int a, int b) is found in the first phase, it will be used. method1(Integer a, Integer b) would only be found in the second phase, if no matching method has been found in the first phase.
4 and 5 are int literals. Therefore they match your method1(int a, int b) without any conversion. Therefore method1(int a, int b) will be chosen.
In order to choose method1(Integer a, Integer b), the compiler would have to box the two int literals to Integers. That can only take place if no method matching your method name and the passed parameters which doesn't require boxing/unboxing conversions is found. Clearly, that's not the case here, since method1(int a, int b) exists. Even a method1(long a, int b) or method1(long a, long b) or method1(int a, long b) would be preferred over method1(Integer a, Integer b), since they don't require boxing conversion.
Both #Eran and #Holger answers are correct and very well explained, but if you want to try your code by yourself, here it is:
public class Answer {
static void method1(int a, int b) {
System.out.println("Inside the method1(int, int)");
}
static void method1(Integer a, Integer b) {
System.out.println("Inside the method1(Integer, Integer)");
}
//Test
public static void main(String[] args) {
method1(4, 5);
method1(Integer.valueOf(4), Integer.valueOf(5));
// method1(4, Integer.valueOf(5)); //Ambiguous method call - won't compile
}
}
The output is:
Inside the method1(int, int)
Inside the method1(Integer, Integer)
Primitives are at it again, breaking rules, I learned before. Well not technically primitive but composed of them.
I learned that whenever there's no method more specific than rest, compile time error occurs as it happens here.
public static void caller(){
z5(); // Error. Neither Integer, nor String is more specific
z5(null); // Error for the same reason
}
public static void z5(Integer...integers){
System.out.println("Integer z5 called");
}
public static void z5(String...strings){
System.out.println("String z5 called");
}
Now comes primitives into the picture.
public static void caller(){
z1(null); // Error cuz [I, [J, [F all are subclass of Object.
z1(); // SURPRISINGLY works and calls the int one. WHY?
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(long...longs){
System.out.println("long z1 called");
}
public static void z1(float...floats){
System.out.println("float z1 called");
}
Expected compile time errors occurs here.
public static void caller(){
z1(null); // Error
z1(); // Error
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(boolean...bools){
System.out.println("bool z1 called");
}
Now my question is, int[], float[], or any array of primitives are not primitive types then Why are they treated differently than other reference types?
--UPDATE--
#john16384 You don't think I read your "Possible duplicate" Varargs in method overloading in Java
Top answer there says You cannot combine var-args, with either widening or boxing. Besides I forgot to mention, OP's code posted there, works fine on my jdk 7.
What exactly is going on which works for (int...is) & (float...fs) but not for (Integer...is) & (Float...fs) and not for (int...is) & (boolean...bool)
Quote from the JLS about varargs invocations when multiple methods are applicable:
15.12.2.5. Choosing the Most Specific Method
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.
The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time error. In cases such as an
explicitly typed lambda expression argument (§15.27.1) or a variable
arity invocation (§15.12.2.4), some flexibility is allowed to adapt
one signature to the other.
The important part here is how methods are defined to be more specific. It basically says that int... is more specific than long... because any values you could pass to the first method could also be passed to the second method.
This will also apply to the case where you pass no arguments. int... will be the most specific (it will even see byte... as more specific!).
public static void main(String[] args) {
bla();
}
private static void bla(long... x) {}
private static void bla(int... x) {}
private static void bla(short... x) {}
private static void bla(byte... x) {} // <-- calls this one
The reason you get an error when also creating an overload boolean... is that it now is ambigious which one to call, and the compiler stops before getting to the point where it has to pick the most specific method.
This question already has answers here:
Varargs Java Ambiguous Call
(2 answers)
Closed 7 years ago.
I am surprised by seeing the output of this code :
public class File
{
public static void main(String[] args)
{
movie();
}
static void movie(double... x)
{
System.out.println("No varargs");
}
static void movie(int... x)
{
System.out.println("One argument");
}
}
It outputs,
One argument
Why is it so ?
I thought that this code would not compile because the call to movie() is ambiguous, but it runs fine and outputs One argument.
If I modify the code to:
public class File
{
public static void main(String[] args)
{
movie();
}
static void movie(boolean... x) //Changed the parameter type to boolean from double
{
System.out.println("No varargs");
}
static void movie(int... x)
{
System.out.println("One argument");
}
}
There is an error message.
Why does the first code run fine, but the second gives an error?
This behaviour is due to the fact that int is more specific than double while there is no such comparison between int and boolean.
As specified in the JLS section 15.12.2.5 (emphasis mine):
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, and 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.
What more specific actually means is later defined with subtyping:
A type S is more specific than a type T for any expression if S <: T.
This means that S is more specific than T is S is a subtype of T. For primitive types, this comes down to the following properties:
double > float
float > long
long > int
int > char
int > short
short > byte
Notice that boolean is not there.
As such,
public static void main(String[] args) {
movie();
}
static void movie(int... x) { }
static void movie(short... x) { }
static void movie(double... x) { }
static void movie(byte... x) { }
compiles and movie(byte... x) will be called because it is the most specific.
However,
public static void main(String[] args) {
movie();
}
static void movie(int... x) { }
static void movie(boolean... x) { }
does not compile because boolean cannot be compared to int.
The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time error.
Consider movie(int...x) as M1 and movie(double...x) as M2.
Method M1 is more specific than M2 because we can call method M2 with the same inputs given to the method M1 directly without any compile time errors.
So, invocation on first method M1 is defineitly handled by the M2. Because double can handle int without any problem.
But we can not invoke M1 with the same inputs given to the method M2 and that is easy to understand.
Let's check following example,
public class Test {
public static void main(String[] args) {
movie();
}
static void movie(int... x) {
System.out.println("One argument");
}
static void movie(short... x) {
System.out.println("Short argument");
}
}
OUTPUT
Short argument
Because here short is more specific than int for method call movie().
On the other hand for boolean method call movie(); raise the confusion, because compiler can not decide which method to call because in this case there is no such point of more specific method.
I think the reason is automatic type promotion in java. By default, the type of expression is promoted to Integer type or Long type (depending upon the range) if your expression does not contain floating point.
So in first case the void expression is simply resolved to int varargs as the Integer wins the contest due to the absence of floating point value in the expression.
In second case however, compiler can not decide what to call, i.e from among the overloaded methods it can not decide which one to call with no-args.
Answer:
1. If we use objects of datatypes like int as Integer it also gives the same error. So the reason is it does not work with Object types and gives errors if no argument is specified for the function.
2. As vararg takes an argument as an Array of a given type. So that's why if you pass no arguments it takes it as an array with zero arguments and executes the integer function because it got one of the functions with an integer argument.
3. It when you put float and double for two different functions and no pass any argument then it executes the float argument function.
Solution:
Do not use object types like String, Integer, Float, Double, Boolean, and Char. Instead use int, float, double, and boolean if want
to execute one of the functions with no arguments passing.
So in the given example we need to specifiy a boolean argument for the Boolean object of non-primitive data type true or false.
public class File
{
public static void main(String[] args)
{
movie(true);
}
static void movie(Boolean...x) //Changed the parameter type to boolean from double
{
System.out.println("No varargs"+x);
}
static void movie(int...x)
{
System.out.println("One argument");
}
}
Here is what I know about overload resolution in java:
The process of compiler trying to resolve the method call from given
overloaded method definitions is called overload resolution. If the
compiler can not find the exact match it looks for the closest match
by using upcasts only (downcasts are never done).
Here is a class:
public class MyTest {
public static void main(String[] args) {
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
}
void TestOverLoad(int a){
System.out.println(8);
}
void TestOverLoad(Object a){
System.out.println(10);
}
}
As expected the output is 10.
However if I change the class definition slightly and change the second overloaded method.
public class MyTest {
public static void main(String[] args) {
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
}
void TestOverLoad(int a){
System.out.println(8);
}
void TestOverLoad(String a){
System.out.println(10);
}
}
The output is 8.
Here I am confused. If downcasting was never to be used, then why did 8 get printed at all? Why did compiler pick up the TestOverLoad method which takes int as an argument which is a downcast from Integer to int?
The compiler will consider not a downcast, but an unboxing conversion for overload resolution. Here, the Integer i will be unboxed to an int successfully. The String method isn't considered because an Integer cannot be widened to a String. The only possible overload is the one that considers unboxing, so 8 is printed.
The reason that the first code's output is 10 is that the compiler will consider a widening reference conversion (Integer to Object) over an unboxing conversion.
Section 15.12.2 of the JLS, when considering which methods are applicable, states:
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.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing [...]
In Java, resolving methods in case of method overloading is done with the following precedence:
1. Widening
2. Auto-boxing
3. Var-args
The java compiler thinks that widening a primitive parameter is more desirable than performing an auto-boxing operation.
In other words, as auto-boxing was introduced in Java 5, the compiler chooses the older style(widening) before it chooses the newer style(auto-boxing), keeping existing code more robust. Same is with var-args.
In your 1st code snippet, widening of reference variable occurs i.e, Integer to Object rather than un-boxing i.e, Integer to int. And in your 2nd snippet, widening cannot happen from Integer to String so unboxing happens.
Consider the below program which proves all the above statements:
class MethodOverloading {
static void go(Long x) {
System.out.print("Long ");
}
static void go(double x) {
System.out.print("double ");
}
static void go(Double x) {
System.out.print("Double ");
}
static void go(int x, int y) {
System.out.print("int,int ");
}
static void go(byte... x) {
System.out.print("byte... ");
}
static void go(Long x, Long y) {
System.out.print("Long,Long ");
}
static void go(long... x) {
System.out.print("long... ");
}
public static void main(String[] args) {
byte b = 5;
short s = 5;
long l = 5;
float f = 5.0f;
// widening beats autoboxing
go(b);
go(s);
go(l);
go(f);
// widening beats var-args
go(b, b);
// auto-boxing beats var-args
go(l, l);
}
}
The output is:
double double double double int,int Long,Long
Just for reference, here is my blog on method overloading in Java.
P.S: My answer is a modified version of an example given in SCJP.
widening beats boxing, boxing beats var-args. In your example, the widening cannot happen, so the boxing it's applied and Integer is unboxed. Nothing unordinary.
Actually in the second example no downcasting is occurred. There occurred the following thing -
1. Integer is unwrapped/unboxed to primitive type int.
2. Then the TestOverLoad(int a) method is called.
In main method you declare Integer like -
Integer i = 9;
Then call -
test.TestOverLoad(i);
Whereas, you have 2 overloaded version of TestOverLoad() -
TestOverLoad(int a);
TestOverLoad(String a);
Here the second overloaded version of TestOverLoad() takes completely different argument String. Thats why Integer i is unboxed to a primitive type int and after that the first overloaded version is called.
All objects in Java extend the class Object, including the class Integer. These two class have the following relationship: Integer "is a(n)" Object because Integer extends Object. In your first example, the method with Object parameter is used.
In the second example, no methods are found that accept an Integer. In this case Java uses what is called auto-unboxing to resolve the Integer wrapper class to a primitive int. Thus, the method with the int parameter is used.
While accepted answer of #rgettman leads to very right source, more precisely, §15.12.2.2 and §15.12.2.3 of the JLS Section 15.12.2 discuss the applicability, not the resolution - what the OP asked for. In the example the OP provided both testOverLoad methods are applicable, .i.e. will be successfully resolved in the absence of another one.
Instead, 15.12.2.5. Choosing the Most Specific Method discusses the resolution of the applicable methods.
It reads:
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:
...
m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter
types T1, ..., Tn, the type Si is more specific than Ti for argument
ei for all i (1 ≤ i ≤ n, n = k).
So, in the first example, provided by OP, for parameter i of type Integer method testOverLoad(Object a) is more specific than testOverLoad(int a).
This is happening due to Widening and Narrowing Type casting
Widening means a small type can be accommodated in a larger type without any loss of information.
Widening Typecasting is automatic.
That means a byte value can be automatically casted to short, int, long or double.
byte->short->int->float->double
Widens from left to right.
Type Casting in Java
Hope this answers your question!
you can check with one more example :
public class HelloWorld {
void show(String c){
System.out.println("int double overloaded method");
}
void show(Object c){
System.out.println("double int overloaded method");
}
}
here the you will get : double int overloaded method
I have the following methods:
static void f(double x)
{
System.out.println("f(double)");
}
static void f(Double xObj)
{
System.out.println("f(Double)");
}
static void f(double... s)
{
System.out.println("f(double...)");
}
public static void main(String[] args)
{
double x1 = 8.5;
Double xO1 = 5.25;
f(x1);
f(xO1);
}
Output:
f(double)
f(Double)
The rule of searching an overloaded method is the following:
Search for the overloaded method without including method with auto (un)boxing and method with ellipsis.
If no method is found, search again with including method with auto (un)boxing.
If no method is found, search again with including method with ellipsis.
This rule is applicable when calling method f with primitive parameter, but when calling with auto boxing parameter this rule is not applicable.
Can anyone explain me if this rule is correct or not ? and what is the correct one ?
Thanks for advance :)
Java picks the most specific method that fits the arguments using this rule
"This rule is applicable when calling method f with primitive parameter, but when calling with auto boxing parameter this rule is not applicable."
When you remove the method f(Double), f(xO1) will call f(double) because that was the only, and the most specific method that fits the arguments.
For reference you may check the Java Language Specification