The first code is perfectly valid. Since you can have a reference of super type hold a subclass object as in below assignment:
Notifiable notifiable = new NotifiableImpl1();
The same way you can add a NotifiableImpl
object to a Set<Notifiable>
. That set can hold object of any subtype.
Second code is not valid as per the PECS rule. Basically a Set<? extends Notifiable>
is a producer of Notifiable
, and not a consumer. You can't add any subtype object to it. That set can actually hold a reference to a HashSet<NotifiableImpl1>
, and you can't add a NotifiableImpl2
object to such set. It would fail at runtime, if it was allowed by compiler. In fact you can't add anything except null
to such types, because the actual type that the Set
will hold is still unknown to the compiler.
Third code is also valid. A Set<? extends Notifiable>
means a Set
of an unknown type that extends from Notifiable
. So, in the following code:
Set<Notifiable> set = new HashSet<>();
Set<? extends Notifiable> set3 = set;
You're simply assigning a concrete parameterized type reference to a bounded wildcard type. Since Notifiable
is a valid substitute for ? extends Notifiable
, that assignment perfectly makes sense.
The reason behind existence of the wildcard types is to allow a single reference point to different kinds of objects. This is the general polymorphic rule. Without wildcard it won't be valid for a Set<Notifiable>
to point to a HashSet<NotifiableImpl1>
, simply because generic types are invariant.