This is due to type erasure. At compile time, when compiling
public <C extends Parent> C get() {
return (C) new ChildA();
}
simply checks that ChildA
is a subtype of Parent
and thus the cast won't definitely fail. It does know that you're on shaky ground, given that ChildA
might not be assignable to type C
, so it issues an unchecked-cast warning letting you know that something could go wrong. (Why does it allow the code to compile, rather than just rejecting it? Language design choice motivated by the need for Java programmers to migrate their old pre-generics code with a minimum of rewriting.)
Now as to why get()
doesn't fail: there is no runtime component to the C
type parameter; after compilation the type argument simply gets erased out of the program and replaced with its upper bound (Parent
). So the call will succeed even if the type argument is incompatible with ChildA
, but the first time you actually try to use the result of get()
as a ChildB
a cast (from Parent
to ChildB
) will occur and you'll get an exception.
The moral of the story: treat unchecked cast exceptions as errors, unless you can prove to yourself that the cast would always succeed.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…