Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics How to accept any derived type in generic parameter

Tags:

java

generics

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

like image 248
bl3e Avatar asked Mar 09 '16 18:03

bl3e


2 Answers

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 putting 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.)

In response to the changes added to the question:

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.

like image 174
rgettman Avatar answered Oct 08 '22 02:10

rgettman


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
like image 1
M A Avatar answered Oct 08 '22 01:10

M A