According to this entry in the Java Generics FAQ, there are some circumstances where a generic method has no equivalent non-generic method that uses wildcard types. According to that answer,
If a method signature uses multi-level wildcard types then there is always a difference between the generic method signature and the wildcard version of it.
They give the example of a method <T> void print1( List <Box<T>> list), which “requires a list of boxes of the same type.” The wildcard version, void print2( List <Box<?>> list), “accepts a heterogenous list of boxes of different types,” and thus is not equivalent.
How do you interpret the the differences between the following two method signatures:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
Intuitively, it seems like these definitions should be equivalent. However, the call f(ArrayList.class) compiles using the first method, but the call g(ArrayList.class) using the second method results in a compile-time error:
g(java.lang.Class<? extends java.lang.Iterable<?>>) in Test
cannot be applied to (java.lang.Class<java.util.ArrayList>)
Interestingly, both functions can be called with each others’ arguments, because the following compiles:
class Test {
<T extends Iterable<?>> void f(Class<T> x) {
g(x);
}
void g(Class<? extends Iterable<?>> x) {
f(x);
}
}
Using javap -verbose Test, I can see that f() has the generic signature
<T::Ljava/lang/Iterable<*>;>(Ljava/lang/Class<TT;>;)V;
and g() has the generic signature
(Ljava/lang/Class<+Ljava/lang/Iterable<*>;>;)V;
What explains this behavior? How should I interpret the differences between these methods’ signatures?
Well, going by the spec, neither invocation is legal. But why does the first one type check while the second does not?
The difference is in how the methods are checked for applicability (see §15.12.2 and §15.12.2.2 in particular).
For simple, non-generic
gto be applicable, the argumentClass<ArrayList>would need to be a subtype ofClass<? extends Iterable<?>>. That means? extends Iterable<?>needs to containArrayList, writtenArrayList <= ? extends Iterable<?>. Rules 4 and 1 can be applied transitively, so thatArrayListneeds to be a subtype ofIterable<?>.Going by §4.10.2 any parameterization
C<...>is a (direct) subtype of the raw typeC. SoArrayList<?>is a subtype ofArrayList, but not the other way around. Transitively,ArrayListis not a subtype ofIterable<?>.Thus
gis not applicable.fis generic, for simplicity let us assume the type argumentArrayListis explicitly specified. To testffor applicability,Class<ArrayList>needs to be a subtype ofClass<T> [T=ArrayList] = Class<ArrayList>. Since subtyping is reflexisve, that is true.Also for
fto be applicable, the type argument needs to be within its bounds. It is not because, as we’ve shown above,ArrayListis not a subtype ofIterable<?>.So why does it compile anyways?
It’s a bug. Following a bug report and subsequent fix the JDT compiler explicitly rules out the first case (type argument containment). The second case is still happily ignored, because the JDT considers
ArrayListto be a subtype ofIterable<?>(TypeBinding.isCompatibleWith(TypeBinding)).I don’t know why javac behaves the same, but I assume for similar reasons. You will notice that javac does not issue an unchecked warning when assigning a raw
ArrayListto anIterable<?>either.