How does Java handle potentially ambiguous method calls?


How does Java handle potentially ambiguous method calls?



Playing around with ambiguous methods calls that have multiple parameters, I noticed that quite often it was not actually ambiguous when I expected it to be, and this led to some strange behavior that I couldn't quite understand.



For example, with the following inheritance structure:


public static class A {
}

public static class B extends A {

}
public static class C extends B {

}



And running the method test().


test()


public static void test() {
test(new C(), new C(), new C());
}



For some reason these two methods are ambiguous


public static void test(A x, A xx, B xxx) {
System.out.println("TEST 1");
}

public static void test(A x, C xx, A xxx) {
System.out.println("TEST 2");
}



However swapping the last two arguments in the second method makes that one take priority.


public static void test(A x, A xx, B xxx) {
System.out.println("TEST 1");
}

public static void test(A x, A xx, C xxx) {
System.out.println("TEST 2"); //No longer ambiguous, this one is called
}



Can someone please explain this behavior, and also in general exactly how quasi-ambiguous method calls are determined in Java with multiple parameters?





I just want to point out that what you have described is not "multiple-inheritance" it is just a chain of single inheritance.
– bhspencer
Jul 1 at 14:52





@bhspencer my bad, I removed the tag
– kmecpp
Jul 1 at 14:53





I'm almost certain there is a duplicate for this. Just review the rules for method overriding.
– Tim Biegeleisen
Jul 1 at 14:53





It's determined by the rules of the Java Language Specifications (docs.oracle.com/javase/specs/jls/se8/html/…). They're long and complex. Nobody knows them all. But it's not a problem, because nobody uses such ambiguous overloads, since they're very hard to get right.
– JB Nizet
Jul 1 at 14:53




4 Answers
4



Reading through the Java Language Specifications chapter on choosing the most specific method, the following statements are important to note:



One applicable method m1 is more specific than another applicable
method m2, for an invocation with argument expressions e1, ..., ek,
if ...



...m2 is not generic, and 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).



What this basically means is that given two (non varargs) method signatures that can match a method call, the compiler will choose the more specific method, and the more specific method is the one where each parameter is more specific than (i.e. either the same class as or a subclass of) the corresponding parameter in the other signature (ignoring generics here for the sake of simplicity).



So, for example when you have (A,A,B) and (A,A,C), the latter is more specific because A = A and C is a subclass of B, so the choice is clear and unambiguous.



But when you have (A,A,B) and (A,C,A), the former can't be more specific because C is a subclass of A, but the latter can't be more specific either because B is a subclass of A. Hence, ambiguity.



Its because both of test methods can accept instances of C as its arguments as C can act as B and A


C


C


B


A


public static void test(A x, A xx, B xxx) {
System.out.println("TEST 1");
}

public static void test(A x, C xx, A xxx) {
System.out.println("TEST 2");
}



The problem here is that both methods apply to (A, C, B). In this specific circumstance ambiguity is present.


(A, C, B)



With the unambiguous example you provided there is no signature common to both declarations:


public static void test(A x, A xx, B xxx) {
System.out.println("TEST 1");
}

public static void test(A x, A xx, C xxx) {
System.out.println("TEST 2"); //No longer ambiguous, this one is called
}



The first applies to (A, A, B), the second applies to (A, A, C). The second is merely overriding the former in providing a more specific use case (signature). You might want to consider it as overriding the former, although that probably isn't the technical term.


(A, A, B)


(A, A, C)



By extension C is a A so given the choice of calling methods where the parameter is C or A the interpreter will call either (hence ambiguity can arise) but must call the one with the more specific parameter, if available. This can be described as polymorphism; the run time type of the instance is used to determine the class and method calls are searched from the descended class up.


C


A


C


A



As JB Nizet points out, the language specs are the authority here, but I like to experiment too.



I try to explain you what would be the problem.



For instance I have a class that contains three methods that with different parameters calculates the multiplication of two numbers.



These methods are:



Because the overloading all these methods are valid and calculate the same thing: the result of the multiplication beetween x and y.



Suppose to have two variables which are the parameters for the method:


int x;
long y;



Calling mul(x, x) leads to this dilemma: I should cast x to long, so calling mul(long ,long) or I should cast y to int, so calling mul(int, int).



This is the problem that you can find while using overloading.



I hope I help you!






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Moria Casán

How to make file upload 'Required' in Contact Form 7?

Quinn's Post Commonwealth War Graves Commission Cemetery