Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics - Java collection within a collection

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.

like image 835
claire Avatar asked Nov 07 '13 15:11

claire


People also ask

Why we use generics in collections in Java?

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.

Are generics only limited to collections?

Every parameterized class can accept wildcards. Just because collections are the most representative example of generics usage, you are not limited to them.

What is generics in collection in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.

What are the limitations of generics in Java?

To use Java generics effectively, you must consider the following restrictions: Cannot Instantiate Generic Types with Primitive Types. Cannot Create Instances of Type Parameters. Cannot Declare Static Fields Whose Types are Type Parameters.


2 Answers

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);
like image 55
Cyrille Ka Avatar answered Oct 13 '22 16:10

Cyrille Ka


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.

like image 31
m0skit0 Avatar answered Oct 13 '22 17:10

m0skit0