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.
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.
Every parameterized class can accept wildcards. Just because collections are the most representative example of generics usage, you are not limited to them.
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.
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.
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);
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.
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