The reason you get a compiler error, for the same reason you cannot add Dog to List<? extends Animal> List<? extends Animal> - you cannot call a method with a common parameter if the type of the reference variable has a wildcard of the upper bound. The type parameter of the map value can refer to any type that matches ? extends Collection<String> ? extends Collection<String> , possibly like HashMap<Integer, LinkedList<String>> . You can legitimately insert this line before calling put :
map = new HashMap<Integer, LinkedList<String>>();
The compiler does not know the exact type that is actually on the map, so at compile time it should be in put ting a TreeSet<String> as the value for the map, the value of which could be something like LinkedList<String> .
In put value in map (except null ), you must remove the wildcard.
HashMap<Integer, TreeSet<String>> map = new HashMap<Integer, TreeSet<String>>();
As JB Nizet commented, you can still put the value of any Collection , such as TreeSet , if you delete the template but save the Collection .
HashMap<Integer, Collection<String>> map = new HashMap<Integer, Collection<String>>();
(In addition, a diamond operator can simplify declarations here.)
In response to the changes added to the question:
Here you used the lower bound.
HashMap<Integer, ? super Collection<String>> map = new HashMap<Integer, TreeSet<String>>());
The type parameter can be Collection<String> or any supertype, such as Object . This prohibits subtypes such as TreeSet<String> . Java generics are invariant. The only reason any variation from Collection<String> is allowed is related to the template.
HashMap<Integer, ? super Collection<String>> map = new HashMap<>(); map.put(1, new TreeSet<String>());
This is allowed because any supertype Collection<String> will match any subtype as an argument. After all, a TreeSet<String> is an Object . A TreeSet<String> can be put as the value of a map , whether it be a HashMap<Integer, Object> or HashMap<Integer, Collection<String>> , or any other type between them. The compiler can prove type safety, so it allows you to make calls.