I'd like to resolve a dubious point that has been affecting some parts of my code for years — it is fixed with a trivial cast, but now I'd like to understand the rationale behind it and possibly a reference in official specifications.
Let me introduce this piece of code:
import java.util.function.Supplier;
class BaseClass<T, R> {
R getSomething() { return null; }
static <U> U method1 (final U arg1) { return arg1; }
static <U> U method2 (final U arg1, final Class<? extends U> arg2) { return arg1; }
static <U> U method3 (final U arg1, final Supplier<? extends U> arg2) { return arg1; }
}
Now let me have a subclass with a partial generics binding (T
is still unbound):
class DerivedClass<T> extends BaseClass<T, String> {
private String s;
void test (final DerivedClass<T> arg) {
final var m1 = method1(arg);
s = m1.getSomething();
final DerivedClass<T> m3 = method1(arg);
s = m3.getSomething();
final var m2 = method2(arg, DerivedClass.class);
// 1. ERROR: requires String, found Object
s = m2.getSomething();
// 2. WARNING: unchecked assignment DerivedClass to DerivedClass<T>
final DerivedClass<T> m4 = method2(arg, DerivedClass.class);
s = m4.getSomething();
final var m5 = method3(arg, () -> new DerivedClass<>());
s = m5.getSomething();
}
}
With method1
everything is fine and the returned object carries the binding R
-> String
. In other words, the U
type of arg1
is correctly propagated to the result.
With method2
the binding is lost (and "downgraded" to Object
): this ends up in a warning if I force things with an explicit type declaration of m4
(of course an explicit cast would work too) and an error if I use var
.
With method3
, in spite of arg2
being declared in a similar way as in method2
, everything is fine again.
So it seems that the culprit is the presence of an argument of Class<>
type. Why? In general, why doesn't the compiler use arg1
for a complete match even when there is a Class<>
parameter?
As far as I can remember this happens since Java 5 (of course the portions with var
refer to Java 17).