public class Demo1{
public static void main(String[] args){
show('A','A');
}
public static void show(char c, long a){
System.out.println("long-char");
}
public static void show(char c, int a){
System.out.println("char-int");
}
}
Output : char-int
But when I change the order of parameters in the first show() method (replacing
public static void show(char c, long a){} with public static void show(long a, char c) {}), I get a compilation error.
The compiler says that it is an ambiguous method call, because it is.
The general approach taken for overload resolution is to find the most specific applicable method, given the number and types of the actual parameters.
In the first case, the two methods have char as their first parameter; so it is only down to choosing whether the int or long overload is more specific, given that the actual parameter is a char: it is the int overload which is more specific, because int is narrower than long.
In the second case, one method has char as the first parameter; one method has char as the second parameter. So, given that the actual parameters are both chars, one of the parameters has to be converted (widened) to invoke either of the methods.
The language spec does not define that one is more specific than the other in such a case; they are both considered equally applicable, so the method call is ambiguous, and thus is a compile-time error.
Related
So, today I've been testing Java's overloading techniques and I've come across ambiguity which I can't explain. Basically, when there is a vararg method with primitive and its corresponding wrapper the compiler complains and can't decide which one to choose and I don't understand why? It's easy for human to decide and not for compiler?
Here is the fragment which works for non-vararg parameters:
public static void main(String[] args)
{
int a = 14;
Integer b = new Integer(14);
stuff(a);
stuff(b);
}
static void stuff(Integer arg) { System.out.println("Integer"); }
static void stuff(int arg) { System.out.println("int"); }
And here comes the vararg which complains and cries like a baby:
public static void main(String[] args)
{
int a = 14;
Integer b = new Integer(14);
stuff(a); // Doesn't compile (ambiguity)
stuff(b); // Doesn't compile (ambiguity)
}
static void stuff(int... arg) { System.out.println("varargs int"); }
static void stuff(Integer... arg) { System.out.println("varargs Integer"); }
Consider the following two hypothetical calls to stuff():
int a = 14;
Integer b = new Integer(14);
stuff(a, b);
stuff(b, a);
How does the compiler even know which method should be called here? Because of autoboxing rules, either call could be referring to either overloaded method.
Update:
My answer is logically correct, or at least on the right track, but for a more formal answer we can refer to this SO question:
Why ambiguous error when using varargs overloading with primitive type and wrapper class?
The two varargs method are invoked in loose invocation context. As a result, the compiler will attempt to find the more specific method via JLS 15.12.2.5 Choosing the Most Specific Method. However, since neither int nor Integer are subtypes of one another, the compiler will throw an error.
The problem is:
java is doing behind the scenes bridge methods (you need to verify that is you need deep info)
AND the important part, vargargs means too YOU CAN JUST NOT PASS ANY PARAMETER, so:
static void stuff(int... arg)
and
static void stuff(Integer... arg)
can both being invoked taking no parameters... so that will create some conflict about what method should the JVM invoke
When method overloading is done, I know we can only create methods with the same name if only their method signatures are different.
class Demo{
public static void myMethod(int y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
Code shown above gives an error saying error: reference to myMethod is ambiguous
This problem occurs because the byte value can be assigned to both int and double types after automatic conversion and it is confusing as to which method the values to be passed right?
Please correct me if i'm wrong..
I tried the following program. I thought this would also give an error but it compiled without an error
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
MyClass.myMethod(100);
}
}
I thought it would also give the same error as earlier but this gave the output as myMethod(int)... so i assumed that since it has a perfectly matching method that can pass the int value, it doesn't give an error..
but what if i make the following changes to the second program above, why doesn't it give an error??
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
byte b=10;
MyClass.myMethod(b);
}
}
the byte can be automatically converted into int and double right? the output was given as myMethod(int)..
shouldn't this be confusing to the compiler and give that error reference to myMethod is ambiguous??
I will not specify and detail all rules used by the compiler to decide which method has to be invoked as it encloses other criteria.
I will only focus on the method parameters criteria that is your question.
In 15.12.2.5. Choosing the Most Specific Method, you have a precious information :
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 type error.
In your example here is the part of the JLS that should answer to your question :
One fixed-arity member method named m is more specific than another
member method of the same name and arity if all of the following
conditions hold:
The declared types of the parameters of the first member method are T1, ..., Tn.
The declared types of the parameters of the other method are U1, ..., Un.
If the second method is generic, then let R1 ... Rp (p ≥ 1) be its type parameters, let Bl be the declared bound of Rl (1 ≤ l ≤ p), let
A1 ... Ap be the type arguments inferred (§15.12.2.7) for this
invocation under the initial constraints Ti << Ui (1 ≤ i ≤ n), and let
Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ n).
Otherwise, let Si = Ui (1 ≤ i ≤ n).
For all j from 1 to n, Tj <: Sj.
If the second method is a generic method as described above, then Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p).
Which should interest you is For all j from 1 to n, Tj <: Sj.
At compile-time, if several applicable methods have been identified, then the most specific one is chosen.
Nevertheless, if more than one method have the maximal specificity with the effective parameter types, the compiler doesn't know what method should be invoked. So it emits a compilation error.
You can find this information in the JLS : 15.12.2. Compile-Time Step 2: Determine Method Signature:
A method is said to be maximally specific for a method invocation if
it is accessible and applicable and there is no other method that is
applicable and accessible that is strictly more specific.
If there is exactly one maximally specific method, then that method is
in fact the most specific method; it is necessarily more specific than
any other accessible method that is applicable. It is then subjected
to some further compile-time checks as described in §15.12.3.
It is possible that no method is the most specific, because there are
two or more methods that are maximally specific. In this case:
- If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:
If exactly one of the maximally specific methods is not declared
abstract, it is the most specific method.
Otherwise, if all the maximally specific methods are declared
abstract, and the signatures of all of the maximally specific methods
have the same erasure (§4.6), then the most specific method is chosen
arbitrarily among the subset of the maximally specific methods that
have the most specific return type.
However, the most specific method is considered to throw a checked
exception if and only if that exception or its erasure is declared in
the throws clauses of each of the maximally specific methods.
- Otherwise, we say that the method invocation is ambiguous, and a
compile-time > error occurs.
If we apply these rules to your three examples (I changed the order to start with cases that compile fine and finish with the compilation error case).
1) One maximally specific method found
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
MyClass.myMethod(100);
}
}
the compiler sees a method with a perfect matching : myMethod(int i) as the value passed is an int. It compiles fine.
2) One maximally specific method found
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
byte b=10;
MyClass.myMethod(b);
}
}
The compiler sees a method with a more higher specificity than the other one.
An implicit conversion from byte to int is indeed more specific than an implicit conversion from byte to double according to widening primitive conversions rules.
We could check that void myMethod(int i) is more specific than void myMethod(double a)if any invocation handled by the first method can be passed on to the other one without a compile-time type error.
myMethod(3); applied to void myMethod(double x) compiles fine.
But myMethod(double)3); applied to void myMethod(int y) produces a compilation error.
So a unique maximally specific method was found : void myMethod(int i).
The compilation is fine.
3) Unique maximally specific method not found
class Demo{
public static void myMethod(int y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
The compiler sees two methods where no one has a specificity higher than the other one.
First, in both cases, implicit conversions of the effective parameters types to the declared types of methods parameters is required.
But in both cases, the specificity is the same.
We could check that void myMethod(int y, double x) is as specific as myMethod(double x,int y) if any invocation handled by the any method cannot be passed on to the other one without a compile-time type error.
myMethod(double)3,4); applied to void myMethod(int y, double x) produces a compilation error as an int variable cannot accept a double value.
And myMethod(3,(double)4); applied to void myMethod(double x,int y) produces a compilation error for the same reason.
No unique maximally specific method was found. So the compiler cannot guess which method should be called. A compilation error occurs.
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
MyClass.myMethod(100);
}
}
The output is myMethod1(int) because the default type for java integer literal is integer. So it takes the closest match and calls myMethod(int i).
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
byte b=10;
MyClass.myMethod(b);
}
}
This wouldn't create an ambiguity for compiler as the parameter you are sending is byte, byte is widened to match with the method parameter. Had you declared myMethod(short i), It would have called this instead of myMethod(int i). This is how widening works in java.
byte->short->int->long
class Demo{
public static void myMethod(int y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
The compiler finds an ambiguity in the above snippet as byte b is widened to int now both the methods have parameter declaration as (double,int) (int,double) so there is an ambiguity as both the declaration has an int variable hence it creates a confusion for the compiler itself. Try to change one of the method declaration to (double,double) and you'll see that it calls the one that hasint` in the declaration. Example of what I am saying is
class Demo{
public static void myMethod(double y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
In this case it will call myMethod(double x, int y)
For more clearance you can refer JLS
It knows to prefer promotion to int over promotion to double, but you've given it a choice between two methods that would both require a promotion to int, so it doesn't know which one you mean.
To understand this kind of problems you should have below threes in mind:
The compiler always tries to choose the most specific method available with least number of modifications to the arguments.
Java designers have decided that old code should work exactly as it used to work before boxing-unboxing functionality became available.
Widening is preferred to boxing/unboxing (because of the above), which in turn, is preferred over var-args.
I am trying to understand how Overloading in JAVA works and trying to get grasp of various overloading rules that are applied in case of widening, autoboxing and varargs in JAVA. I am not able to understand what is happening in the following scenario:
package package1;
public class JustAClass {
public static void add(int a, long b) {
System.out.println("all primitives");
}
//public static void add(Integer a, long b) {
// System.out.println("Wraper int, primitive long");
//}
public static void add(int a, Long b) {
System.out.println("Primitive int, Wrapper long");
}
public static void add(Integer a, Long b){
System.out.println("All wrapper");
}
public static void main(String[] args) {
int a = 10;
Integer b = 10;
long c = 9;
Long d = 9l;
add(a,c);
add(a,d);
add(b,c);
add(b,d);
}
}
At this point, I get a compilation error at the third invocation of the add method saying The method is ambiguous for the type Error .
Why is this so? What are the rules for determining which invocation of method will work? What is exactly happening in the following case?
I feel that fourth overloaded add method should work. Please help me understand the concept behind this.
There are 3 stages to method overloading resolution. The first stage doesn't do auto-boxing/unboxing, which means methods that require boxing/unboxing of the passed parameters in order to match one of the overloaded versions of add will only be considered if no match was found that doesn't require boxing/unboxing. That's why 3 of your calls, which have a single exact match, work. Regarding add(b,c);, see below why it's ambiguous.
add(a,c); // exact match to add(int a, long b)
add(a,d); // exact match to add(int a, Long b)
add(b,c); // there is no exact match, so at least one of the passed parameters must
// be boxed or unboxed. However, by unboxing b to int or boxing
// c to Long, each of the three add methods can match, and the
// compiler doesn't know which one to prefer
add(b,d); // exact match to add(Integer a, Long b)
Could anyone explain to me why Java is picking the second overload instead of the first?
public static void foo (int a, double b, double...c) {}
public static void foo (double...a) {}
public static void bar ()
{
// this is the second
foo(1);
}
I thought when I pass 1 as the argument, Java would have picked the first argument because int is more specific than double?
Thanks
The second method is the only one that can match. You have only 1 argument. The first foo has at least two required: an int and a double, so that can't match.
The second foo matches because any number of numbers can match. Java will implicitly promote your int 1 to a double so it can match, with method invocation conversion.
So i can pass nulls into functions in java. I can also overload functions in java. But consider the following
public static void main(String ... args){
doStuff(null);
}
public static void doStuff(String s){
solveWorldHunger();
}
public static void doStuff(Integer i){
nukeCanada();
}
public static void nukeCanada(){
System.out.println("NO!");
}
public static void solveWorldHunger(){
System.out.println("YAY!");
}
The previous program will always print out YAY no matter what the order of the sorce code...
Can anyone shed some light onto why the jvm consitently decides to run the solveWorldHunger function over the nukeCanada function`?
It's quite clear to the compiler that you can't store null in a primitive int. So, it decides to go with the method which has String as argument.
However, when you change your 2nd method to take Integer as argument, then you will get compiler error. Because both of them are eligible to be invoked with null argument. So, there will be ambiguity.
So, try changing your 2nd method: -
public static void doStuff(String s){
solveWorldHunger();
}
public static void doStuff(Integer i){ // Integer instead of `int`
nukeCanada();
}
And invoke it on null. You will see the compiler error.
So, in that case, you can invoke the appropriate method using typecasting like this: -
doStuff((Integer)null); // Calls Integer version
doStuff((String)null); // Calls String version
Also, note that, if you have your 2nd method take a parameter that is a super type of String, like Object, then there would be no ambiguity, because compiler will now choose the most specific one, i.e., String argument.
So, ambiguity only occurs, when you have two methods with types that are not in the same inheritance hierarchy.
Only your doStuff(String) method can take a NULL argument... the primitive int can never be null. If you had a second method overload that passed an object, you would no longer be able to pass null as a parameter for that method.