9

I'm new to generics, so not sure where I'm going wrong...

I have classes, called Cat, Dog and Rabbit, which implement the interface Animal.

The following code will compile

Set<? extends Animal> animalSet;
Set<Dog> dogSet = new HashSet<Dog>();
animalSet = dogSet;

But the following code will not

Map<String, Set<? extends Animal>> animalMap;
Map<String, Set<Dog>> dogMap = new HashMap<String, Set<Dog>>();
animalMap = dogMap; // this line will not compile

The compiler says the types are incompatible. Where am I going wrong?

UPDATE

Thanks for everyone's help

I've changed the first line of code by adding another wildcard The following code will compile

Map<String, ? extends Set<? extends Animal>> animalMap;
Map<String, Set<Dog>> dogMap = new HashMap<String, Set<Dog>>();
animalMap = dogMap;

See also the solution given by Cyrille Ka below - use putAll() to transfer values from dogMap to animalMap, instead of assigning dogMap to animalMap.

claire
  • 93
  • 6
  • Here a parent child relationship does not work as in Parent p = child. – Raúl Nov 07 '13 at 15:07
  • 2
    related: [Multiple wildcards on a generic methods makes Java compiler (and me!) very confused](http://stackoverflow.com/questions/3546745/multiple-wildcards-on-a-generic-methods-makes-java-compiler-and-me-very-confu) – Paul Bellora Nov 07 '13 at 15:17
  • You need to go deeper. – user Nov 09 '13 at 03:20

7 Answers7

7

Basically, when you write:

Map<String, Set<? extends Animal>> animalMap;

You declare that any value of the map is a set that can contain any object whose type is a subclass of Animal. Then it would be perfectly reasonable for a client code to write:

animalMap.put("miaow", aCatSet);

where acatSet is a Set<Cat>.

But dogMap can not accept a Set<Cat> as value, it can only accept Set<Dog>. Therefore there is a possibility of type incompatibility and that's why this construction is forbidden.

Edit: As for how to fix that, it depends on what you want to do. If you have a dogMap somewhere and want to put its content into animalMap then you can simply copy the content like this:

Map<String, Set<? extends Animal>> animalMap = new HashMap<String, Set<? extends Animal>>();
Map<String, Set<Dog>> dogMap = new HashMap<String, Set<Dog>>();

/// fill dogMap

animalMap.putAll(dogMap);
Alexis Pigeon
  • 7,423
  • 11
  • 39
  • 44
Cyrille Ka
  • 15,328
  • 5
  • 38
  • 58
3

Because Java is strongly typed: ? extends Animal is not Dog, it could be Cat, or another subclass.

For example, in this case, if you define Set<? extends Animal> animalSet, I can legally do animalSet.add(new Cat()), right?. But you want to initialize this animalSet with a Set<Dog>, which would not allow animalSet.add(new Cat()) anymore. This is inconsisent, and this is why it is not allowed.

m0skit0
  • 25,268
  • 11
  • 79
  • 127
2

A solution to your problem is this one:

Map<String, ? extends Set<? extends Animal>> animalMap;
Map<String, Set<Dog>> dogMap = new HashMap<String, Set<Dog>>();
animalMap = dogMap;

because Set<Dog> is not a Set<? extends Animal>, but rather extends it.

nelly
  • 417
  • 3
  • 9
1

As for why - let me get back on that later. For now note that this compiles:

Map<String, Set<? extends Animal>> animalMap;
Map<String, Set<? extends Animal>> dogMap 
    = new HashMap<String, Set<? extends Animal>>();
Set<Dog> dogMapVal = new HashSet<Dog>();
dogMap.put("fido", dogMapVal);
animalMap = dogMap; 
rzymek
  • 9,064
  • 2
  • 45
  • 59
  • Thanks. I can understand why your code compiles. But I wanted "dogMap" to just contain sets of dogs, perform some operations on it which are just relative to dogs not all animals, then assign it to "animalMap" later. – claire Nov 07 '13 at 15:13
1

Applying the following logic (based on this answer) to your question :

  • Dog IS Animal
  • Set<Dog> IS Set<? extends Animal>
  • Map<String, Dog> IS NOT Map<String, Animal>

Therefore :

  • Map<String, Set<Dog>> IS NOT Map<String, Set<? extends Animal>>

That is what compiler complains about in your last assignment.

Community
  • 1
  • 1
kiruwka
  • 9,250
  • 4
  • 30
  • 41
0

Here a parent child relationship does not work as in Parent p = child. oracle docs

Raúl
  • 1,542
  • 4
  • 24
  • 37
0

Beacuse the Dog not equals Animal, only extends it.

Image, if there a code where You use somewhere this code, where You put dogs in it. after , somwwhere again yout wold like to use it again, you thinks you put dog here, so you got back dogs. But if someone, put Cats or Rabbits to it?

So this is why.

ronhks
  • 141
  • 2
  • 9