Dilini Peiris
Dilini Peiris

Reputation: 456

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

Upvotes: 5

Views: 1186

Answers (4)

davidxxx
davidxxx

Reputation: 131466

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.

Upvotes: 4

user207421
user207421

Reputation: 310978

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.

Upvotes: 0

BeginnersSake
BeginnersSake

Reputation: 680

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

Upvotes: 1

ddarellis
ddarellis

Reputation: 3960

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.

Upvotes: -1

Related Questions