Given the following declarations
interface Base<A> { }
interface Special<A,B> extends Base<A> { }
<T> void foo(Base<T> b) {}
<T> void foo(Special<?,T> s) {}
Why do I get a compile error for the following code:
Special<String, Integer> s = null;
foo(s); // error: reference to foo is ambiguous
BTW, the problem can be fixed by changing the declaration of the second method to
<T,X> void foo(Special<X,T> s) {}
First of all, a very interesting question.
Consider the following code:
interface NoGenericsBase { }
interface NoGenericsSpecial extends NoGenericsBase { }
interface NoGenericsSuperSpecial extends NoGenericsSpecial { }
void foo(NoGenericsBase b) {
System.out.println("Feel tha base");
}
void foo(NoGenericsSpecial s) {
System.out.println("Special delivery");
}
We've always been taught that the compiler picks the most specific method. And indeed, the abovementioned code will, provided you call foo((NoGenericsSuperSpecial) null)
, print the following:
Special delivery
So far, so good.
Now, let's test some generics behaviour:
interface Base<T> { }
interface Special<T> extends Base<T> { }
void foo(Base<? extends Number> b) {
System.out.println("Feel tha base");
}
void foo(Special<? extends Number> s) {
System.out.println("Special delivery");
}
public static void main(String[] args) {
Special<Integer> v = null;
new Main().foo(v);
}
This code compiles. The compiler finds two matches – both Base<? extends Number>
as Special<? extends Number>
apply –, but the compiler is able to figure out which is the most specific: it will pick void foo(Special<? extends Number>)
, because both captures of the unbound wildcards are equal.
But let us rewrite foo(Base<...>)
method and leave the remaining untouched:
void foo(Base<? extends Integer> b) {
System.out.println("Feel tha base");
}
Now the following error occurs:
reference to foo is ambiguous
both methodfoo(Base<? extends Integer>)
and methodfoo(Special<? extends Number>)
match
Before figuring out the most specific type matching, the compiler handles type variables. Apparently, the compiler cannot figure out whether <? extends Number>
or <? extends Integer>
applies, unregarded the type of the variable itself (Base
or Special
).
It seems that the variable type handling comes prior to the selection of method signatures regarding inheritance.
One (or at least I myself) should expect the compiler to pick foo(Special<? extends Number>)
, but this is not the case.
I do not know whether the compiler is not able to pick the most specific one regarding generics, or it's not configured to do so.
See the Java Language Specification § 18.5 or § 4.5.1 for more details.
Let me take some time to read more of generics, so maybe we can figure it out...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With