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.

4
  • 2
    Contrary to what you think, ? extends Number will allow Number objects. The ? extends X bounded wildcard means "X or a subclass of X". Commented Nov 24, 2009 at 15:21
  • Incidentally, Number is a <? extends Number> so even if inserting into a wildcard list was possible, it wouldn't achieve what you want. The fact that Number is abstract will. Commented Nov 24, 2009 at 15:22
  • You could do ((Map<Integer, Number>) map).put(key, item); if yu are sure about the type. Commented Nov 24, 2009 at 15:26
  • Thanks for pointing out that Number objects would be allowed in the map. It appears that I need to remove the wildcard and just use Number. Commented Nov 24, 2009 at 15:40

3 Answers 3

10

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.

Sign up to request clarification or add additional context in comments.

5 Comments

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.
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 A great example of why Java has deviated from its roots of being easy to understand!
Joshua Bloch, who led the JSR that introduced generics, had stated that wildcards were a mistake: artima.com/weblogs/viewpost.jsp?thread=222021
7

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.