In The below 2 lines of code
HashMap<Integer, ?extends Collection<String>> map=
new HashMap<Integer, TreeSet<String>>();
map.put(1,new TreeSet<String>());
Line 2 :The method put(Integer, capture#1-of ? extends Collection) in the type HashMap> is not applicable for the arguments (int, TreeSet)
Line 1: this has no error.
Why is the same generic type (TreeSet< String >) allowed in Line 1 but not allowed in Line 2?
Edit : With super instead of extends ,why is the following is NOT allowed.
HashMap<Integer, ?super Collection<String>> map=new HashMap(<Integer, TreeSet<String>>());
but
HashMap<Integer, ?super Collection<String>> map=new HashMap();
map.put(1,new TreeSet<String>());
is allowed
The reason you get a compiler error is the same reason you cannot add a Dog
to a List<? extends Animal>
-- you cannot call a method with a generic parameter when the type of the reference variable has an upper bound wildcard. The map
variable's value type parameter could refer to any type that matches ? extends Collection<String>
, perhaps as HashMap<Integer, LinkedList<String>>
. You could legally insert this line before the call to put
:
map = new HashMap<Integer, LinkedList<String>>();
The compiler doesn't know the exact type that's really in the map, so it must, at compile time, prevent you from put
ting a TreeSet<String>
in as a value to a map whose value could be something like LinkedList<String>
.
To put
a value in map
(besides null
), you must remove the wildcard.
HashMap<Integer, TreeSet<String>> map =
new HashMap<Integer, TreeSet<String>>();
As JB Nizet has commented, you still can put a value of any Collection
such as TreeSet
if you remove the wildcard but keep Collection
.
HashMap<Integer, Collection<String>> map =
new HashMap<Integer, Collection<String>>();
(Also, the diamond operator can simplify the declarations here.)
Here, you've used a lower bound.
HashMap<Integer, ? super Collection<String>> map = new HashMap<Integer, TreeSet<String>>());
The type parameter may be Collection<String>
or any supertype, such as Object
. This disallows subtypes such as TreeSet<String>
. Java's generics are invariant. The only reason any variation from Collection<String>
is allowed is because of the wildcard.
HashMap<Integer, ? super Collection<String>> map = new HashMap<>();
map.put(1, new TreeSet<String>());
This is allowed because any supertype of Collection<String>
will match any subtype as an argument. After all, a TreeSet<String>
is an Object
. A TreeSet<String>
can be put
as a value to map
, whether it's a HashMap<Integer, Object>
or a HashMap<Integer, Collection<String>>
, or any type in between. The compiler can prove type safety, so it allows the call.
The declaration of the map tells the compiler that a value inside in the map is some collection of strings. At runtime, it could be a TreeSet
but it could also be some other collection type. Hence the compiler cannot allow putting a TreeSet
since it may actually contain ArrayList
values.
More generally, whenever you use a bound type argument using the ?
wildcard, you are practically only allowed to read from the map (e.g. iterate over its elements). In other words, you can always do:
for(Iterator<? extends Collection<String>> iterator = map.values().iterator(); iterator.hasNext();) {
Collection<String> collection = iterator.next();
...
}
In your case, however, since you're adding a TreeSet
it means that most likely you know that the map will contain TreeSet
values, so you don't need to use a wildcard:
HashMap<Integer, TreeSet<String>> map = new HashMap<>(); // In Java 7+, you can use the diamond operator when creating the HashMap
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