1

Consider the following classes:

interface Notifiable {

}

class NotifiableImpl1 implements Notifiable {

}

class NotifiableImpl2 implements Notifiable {

}

class NotifiableImpl3 implements Notifiable {

}

It is normal that the following code works:

Set<Notifiable> set = new HashSet<>();
set.add(new NotifiableImpl1());
set.add(new NotifiableImpl2());
set.add(new NotifiableImpl3());

However, the following code does not work:

Set<? extends Notifiable> set2 = new HashSet<>();
set2.add(new NotifiableImpl1());
set2.add(new NotifiableImpl2());
set2.add(new NotifiableImpl3());

I understand it does not work, because it should only be possible to add one concrete subtype of Notifiable to set2, but how does it come then, that the following code also does not work?

Set<? extends Notifiable> set2 = new HashSet<>();
set2.add(new NotifiableImpl1());

And maybe more interesting, why does the following work?

Set<Notifiable> set = new HashSet<>();
set.add(new NotifiableImpl1());
set.add(new NotifiableImpl2());
set.add(new NotifiableImpl3());

Set<? extends Notifiable> set3 = set;
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • I think you can found answers [here](http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs). I like this statements `1st use an extends wildcard when you only get values out of a structure use a super wildcard when you only put values into a structure and don’t use a wildcard when you both get and put.` – nachokk Mar 23 '14 at 19:11
  • @nachokk This question is not about super vs extends. While the answer could potentially be similar, it is does not mean that the question is similar. – skiwi Mar 23 '14 at 19:14
  • That's true, it's not the same question but the same explanation as an answer. – nachokk Mar 23 '14 at 19:15
  • Pinched from the mentioned thread "You actually cannot add anything to a Collection extends Thing>, because you cannot know at runtime which specific subtype of Thing the collection holds." – Salix alba Mar 23 '14 at 19:21
  • You can do `Set extends Bar> set2 = new HashSet< Notifiable >()`, `Set extends Bar> set2 = new HashSet()`. – Salix alba Mar 23 '14 at 19:30

1 Answers1

3

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.

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525