Suppose we have a class A, and a class B, which inherits from class A. Let's say we have:
Set<A> setOfAs = new HashSet<>();
The following casting:
((Set<B>) setOfAs)
will give us run-time error.
However, if we use wildcard and define the following set:
Set<? extends A> setOfAs = new HashSet<>();
we have no problem to do the casting:
((Set<B>) setOfAs)
Why casting a collection of a wildcard is allowed, while casting a collection of "regular" type is forbidden?
we have no problem to do the casting:
You will have an unchecked cast warning, so it's not really the case that you have no problem; it's just that the compiler can't prove it's definitely wrong, and can't put anything into the bytecode to catch the fact it is wrong at runtime.
A Set<? extends T>
is a Set
where it can be assumed that all members can be safely cast to T
without a ClassCastException
.
A Set<? super T>
would be a Set
where it is known to be safe to add a T
to it without causing a ClassCastException
in places relying on the type of elements in the Set
(I believe the correct technical term for this is without causing heap pollution).
A Set<T>
is the intersection of these two bounded types: you can add instances of T
to it, and all elements in it are instances of T
.
By these definitions, a Set<B>
can act as a Set<? extends A>
, because anything that can be cast to an B
can also be cast to A
.
However, a Set<A>
cannot act as a Set<B>
, because it may contain instances of A
which aren't instances of B
.
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!"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With