Reputation: 1266
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
Upvotes: 7
Views: 2570
Reputation: 16390
After reading the other answers and thinking some more about the original question (which was really about having the "? extends
" part versus not having it), I came up with the following example, which should be easier to understand.
Suppose we have this method:
void doSomething(Collection<? extends Number> numbers) { ... }
And the following usages elsewhere:
Collection<Integer> integers = asList(1, 2, 3);
Collection<Long> longs = asList(1L, 2L, 3L);
doSomething(integers);
doSomething(longs);
All of the above compiles fine. Now, let's ask, what can the doSomething
method do with the collection it receives? Can it add some number to it? The answer is no, it can't. And that's because it would be unsafe; for example, if it added an Integer
(which is a Number
), then the doSomething(longs)
call would fail. So, to prevent such possibilities the compiler disallows any attempt to add any Number
at all to a Collection<? extends Number>
.
Similarly, what if the method was declared as:
void doSomething(Collection<Number> numbers) { ... }
In this case, the method can add any number to the collection, but then calls like doSomething(integers)
become compiler errors, since a collection of Integer
can only accept integers. So, again, the compiler prevents the possiblity for ClassCastException
s later, ensuring the code is type safe.
Upvotes: 0
Reputation: 178363
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.
Upvotes: 3
Reputation: 7890
Compiler says:
no suitable method found for put(int,TreeSet) map.put(1, new TreeSet()); method HashMap.put(Integer,CAP#1) is not applicable (actual argument TreeSet cannot be converted to CAP#1 by method invocation conversion) method AbstractMap.put(Integer,CAP#1) is not applicable (actual argument TreeSet cannot be converted to CAP#1 by method invocation conversion) where CAP#1 is a fresh type-variable: CAP#1 extends Collection from capture of ? extends Collection
I think it's because of type ambiguity for compiler and as AR.3 sayed:
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.
If the type be certain for the compiler the code will compiles and runs without any problem, for instance you can write your code as below which compiles and runs successfully:
public <T extends Collection<String>> void someVoid(){
//...
//...
HashMap<Integer, T > map
= new HashMap<>();
map.put(1,(T) (new TreeSet<String>()));
//...
//...
}
Upvotes: 0
Reputation: 72884
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
Upvotes: 1