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?