tags:

views:

162

answers:

3

For the life of me I cannot understand why the compiler won't let me do the following...

import java.util.HashMap;
import java.util.Map;

public class TestMap {
   private final Map<Integer, ? extends Number> map = new HashMap<Integer, Number>();

   public void put(Integer key, Long item) {
      this.map.put(key, item);
   }
}

Why does line this.map.put(key, item) cause a compile error?

I know that I can change the declaration of map to use Number rather than ? extends Number to make it work but it seems to me that what I am doing is perfectly legal, and I would prefer to not allow Number objects in the map. I am using Java 1.6.0_13.

+9  A: 

You can't insert into collections that use wildcard types. This is because, while a List<Float> can be passed to a method that accepts List<? extends Number>, it isn't safe to insert a Long into it. In your case, one might expect the compiler to know better as the definition of the collection is so visible, but it doesn't.

Andrew Duffy
Explained here: http://java.sun.com/docs/books/tutorial/extra/generics/wildcards.html
Pesto
The compiler can't allow it because it can't take responsibility for where that map ends up being passed or used. Sure as a human we can eyeball the program and say it isn't used in any way that matters, but the compiler can only look at the put against the type declaration.
Yishai
You can insert into collecitons that use wildcard types. You just have to do it correctly. The PECS rule: Producer Extends Consumer Super, which means if you are getting from the collection, then use <? extends Number>, if you are putting objects into it, then use <? super Number>. If you have List<? super Number>, you can insert a Long into it. It allows you the flexibility to pass in List<Object> or List<Number>, etc.
Chris Lacasse
@Chris Lacasse A great example of why Java has deviated from its roots of being easy to understand!
John Topley
Joshua Bloch, who led the JSR that introduced generics, had stated that wildcards were a mistake:http://www.artima.com/weblogs/viewpost.jsp?thread=222021
Andrew Duffy
+6  A: 

This is related to generics covariance.

When you declare,

Map<Integer, ? extends Number> map

you can't insert anything to the map because you can't guarantee that ? extends Number is a Long, for instance.

Imagine this situation:

   Map<Integer, ? extends Number> map = new HashMap<Integer, Integer>();

   public void put(Integer key, Long item) {
      this.map.put(key, item);
   }

Here, Integer != Long and yet both obey ? extends Number.

bruno conde
A: 

Supplying the wildcard to a generic type effectively makes it read-only.

John Topley