I was recently caught flat-footed by the following Java code:
interface Common {}
interface A extends Common {}
static class B implements Common {}
static class Impl {
private A a;
public <T extends A> T translate() {
return (T) a;
}
}
static class Usage {
public void use() {
Impl impl = new Impl();
B b = impl.translate(); // Why does this compile?
}
}
I would have expected that the type constraint on Impl.translate
would not allow storing the result in type B
to be accepted by the compiler, considering that B
does not extend A
.
Instead of a compiler error, the code throws an UncheckedCastException
at runtime.
This only happens when the method returns the type T
; if it is the method parameter instead:
public <T extends A> void translate(T t) {}
Then instances of B
are not allowed as parameters to translate
, as expected.
What's going on here? Why is this allowed by Java's type system?