Friday 25 November 2016

When passing arguments to methods become ambiguous in Java?




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??


Answer




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.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...