The whole idea of casting is saying "I know more than you, compiler!" when there's some uncertainty about the actual type of an object.
In the second case, this makes total sense. The compiler knows that setOfAs
is of type Set<? extends A>
, which means "a Set
of an unknown type, and that unknown type extends A
". There's uncertainty as to what type of HashSet
it could be. As far as the the compiler is concerned, it could be HashSet<B>
...
Set<? extends A> setOfAs = new HashSet<A>();
but it also could be HashSet<A>
...
Set<? extends A> setOfAs = new HashSet<B>();
You, by means of casting, say "No, setOfAs
is a HashSet<B>
". The compiler goes, "Well, it could be that, so I'll trust you". Whether your extra knowledge is actually correct, is a separate matter.
In the first case however, setofAs
is of type HashSet<A>
. Since a variable of type HashSet<A>
can never store an object of type HashSet<B>
, i.e. this does not compile:
Set<A> setOfAs = new HashSet<B>();
There is no uncertainty about the generic parameter of Set
. It's gotta be A
. You, trying to cast to HashSet<B>
, will only result in the compiler saying "No, it can never be that!"