This discussion is archived
5 Replies Latest reply: Jan 18, 2011 1:44 PM by 796440 RSS

Can't put instances to "? extends" map.

416044 Newbie
Currently Being Moderated
Here, I tried to reproduce my problem with a simple example:
import java.util.HashMap;
import java.util.Map;

public class Test {

    private class A {
        protected int a = 0;
    }
    
    private class B extends A {
        private int b = 0;
    }
    
    private void method(Map<String, ? extends A> m) {
        m.put("", new B());  //<--- doesn't work
        m.put("", new A());  // <--- doesn't work
    }
    
   public void test(String[] args) {
        Map<String, B> mapB = new HashMap<String, B>();
        method(map);
    }
}
the two lines where I try to but objects into the map, I get
The method put(String, capture#1-of ? extends Test.A) in the type Map<String,capture#1-of ? extends Test.A> is not applicable for the arguments (String, Test.B)
As my "method()" should accept different Maps with differents as for example <String, B>, I needed to change the method parameter to "? extends A". I also tried only "<String, ?>" and "<String, ? extends Object>" but it didn't help.

I found this explanation inthe Java Tutorial (http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html):
There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method. For instance, this is not allowed:

    public void addRectangle(List<? extends Shape> shapes) {
        shapes.add(0, new Rectangle()); // Compile-time error!
    }

You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is ? extends Shape-- an unknown subtype of Shape. Since we don't know what type it is, we don't know if it is a supertype of Rectangle; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there. 
but I don't understand it, as Rectangle is a subclass of Shape, so IT IS a shape and should be savely be putted in the map of "<? extends Shape>".
  • 1. Re: Can't put instances to "? extends" map.
    JoachimSauer Journeyer
    Currently Being Moderated
    I'll explain your problem with Collections, as the first parameter of your map doesn't actually come into play here and it's easier this way.

    A Collection<Number> is guaranteed to contain only Number objects and to accept any Number object (such as Integer, Double, ...)
    A Collection<?> doesn't make any guarantees about its content (except, obviously, that it contains Objects) and doesn't accept any objects to be added (because the type that would be accepted is unknown).
    A Collection<? extends Number> is guaranteed to contain only Number objects (specifically only objects of some unknown subtype of Number). It also accepts only objects of that unknown subtype of Number. Since that subtype is unknown, you can't ever prove that the object you pass in is of the correct type. There is one little exception however: null can be cast to every type, so it is acceptable to add here.

    Simplified answer: Using <? extends Foo> with Collections/Maps means that you can't add anything to them (except for null).

    Why would you want to do that? Easy: if you have a method that walks through an Collection of Number objects to find one that fits some criteria, you could specify the argument to that method as Collection<Number>. Easy enough, right? But if, later on, you've got a Collection<Integer> that you'd like to search using the same method, you can't because a Collection<Integer> is not a Collection<Number>. If, instead, you define your method as taking a Collection<? extends Number>, then it would accept both collections (Collection<Number> and Collection<Integer>). You wouldn't be able to add a new object to that collection, but since its task is to search the collection only, that doesn't matter here.
  • 2. Re: Can't put instances to "? extends" map.
    jtahlborn Expert
    Currently Being Moderated
    one easy way to think about it is that a gneric specified as "?" or "? extends SomeType" is a "read-only" type. so, you can get data from a "? extends SomeType" collection, but you can't put data into it.
  • 3. Re: Can't put instances to "? extends" map.
    416044 Newbie
    Currently Being Moderated
    I don't understand this sentence:

    "It also accepts only objects of that unknown subtype of Number. Since that subtype is unknown, you can't ever prove that the object you pass in is of the correct type."

    Of course I can! Integer is a Number, so Integer is of the correct type 'Number' - as any other subclass of Number.

    If I have Collection<? extends Number> I should be able to add Integer and Double! The result is still Collection<Number> and I would be able to walk through the collection like this:

    for (Number n : myCollection) {
    }
  • 4. Re: Can't put instances to "? extends" map.
    jtahlborn Expert
    Currently Being Moderated
    mh**** wrote:
    I don't understand this sentence:

    "It also accepts only objects of that unknown subtype of Number. Since that subtype is unknown, you can't ever prove that the object you pass in is of the correct type."

    Of course I can! Integer is a Number, so Integer is of the correct type 'Number' - as any other subclass of Number.
    nope, this is a common misconception.

    - Collection<Number> -> this is a collection into which any subclass of a Number can be added (so, you can add a Double or an Integer)
    - Collection<? extends Number> -> this is a collection of a specific subclass of Number which is unknown to the current code context. it could be a Collection<Number>, or a Collection<Integer>, or a Collection<Double>. you can safely read the contents of the collection (casting to Number). however, since the current code does not know how the original collection was defined, you cannot safely write to the collection or you might break the type-safety (e.g. if it is a Collection<Double> and you add an Integer).
    If I have Collection<? extends Number> I should be able to add Integer and Double! The result is still Collection<Number> and I would be able to walk through the collection like this:
    "Collection<Number>" is not the same as "Collection<? extends Number>" (as i detailed above).

    please read the "java generics faq" as it details this and many more specifics related to working with generics.
  • 5. Re: Can't put instances to "? extends" map.
    796440 Guru
    Currently Being Moderated
    jtahlborn wrote:
    If I have Collection<? extends Number> I should be able to add Integer and Double! The result is still Collection<Number> and I would be able to walk through the collection like this:
    "Collection<Number>" is not the same as "Collection<? extends Number>" (as i detailed above).
    Exactly.

    Collection<? extends Number> is either Collection<Integer> or Collection<Double> or Collection< ... etc. ...> or maybe even Collection<Number>, but the compiler doesn't know which one, so it can't allow you to add Integer or Double or anything else, because it could be wrong.

    The very common error that the OP is making is thinking that <? extends Number> means "any mix of any classes that extend number" when in fact it means "some +particular, unknown+ subtype of Number"

    Edited by: jverd on Jan 18, 2011 1:42 PM

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points