Issue
Let's say we have these 3 classes:
class A { }
class B extends A { }
public class App {
static void f(int i, A a) { }
static void f(float j, B b) { }
static public void main() {
int i = 0;
B b = new B();
App.f(i, b);
}
}
This produces the error:
App.java:11: error: reference to f is ambiguous
App.f(i, b);
^
both method f(int,A) in App and method f(float,B) in App match
1 error
Why does it not choose the type f(int, A)
since i
is an integer?
Solution
It is ambiguous because of two reasons:
- both overloads are applicable, and;
- neither overload is more specific than the other
Notice that both the f(int, A)
overload and the f(float, B)
overload can be called with the parameters (i, b)
, since there is an implicit conversion from int
to float
, and an implicit conversion from B
to A
.
What happens when there are more than one applicable method? Java is supposed to choose the most specific method. This is described in §15.12.2.5 of the language spec. It turns out that it is not the case that one of these overloads are more specific than the other.
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 generic [...]
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).
m2 is not generic, and m1 and m2 are applicable by variable arity invocation [...]
Only the second point applies to the two overloads of f
. For one of the overloads to be more specific than the other, every parameter type of one overload has to be more specific than the corresponding parameter type in the other overload.
A type S is more specific than a type T for any expression if S <: T (§4.10).
Note that"<:" is the subtyping relationship. B
is clearly a subtype of A
. float
is actually a supertype (not subtype!) of int
. This can be derived from the direct subtyping relations listed in §4.10.1. Therefore, neither of the overloads is more specific than the other.
The language spec goes on to talk about maximally specific methods, which doesn't really apply to f
here. Finally, it says:
Otherwise, the method invocation is ambiguous, and a compile-time error occurs.
More Examples
static void f(int x) {}
static void f(float x) {}
when called with an int
are not ambiguous because the int
overload is more specific.
static void f(int x, B a) {}
static void f(float x, A a) {}
when called with argument types (int, A)
are not ambiguous because the (int, B)
overload is more specific.
static void f(int x, A a) {}
static void f(float x, A a) {}
when called with argument types(int, A)
are not ambiguous because the (int, A)
overload is more specific. Note that the subtyping relationship is reflexive (i.e. A
is a subtype of A
).
Answered By - Sweeper
Answer Checked By - Dawn Plyler (JavaFixing Volunteer)