This discussion is archived
6 Replies Latest reply: Jan 17, 2011 3:10 AM by 831231 RSS

Map iterator

831231 Newbie
Currently Being Moderated
Dear forum,

I have some trouble with Java generics I cannot figure out myself.
What I have is a method that takes a Map<String, ? extends Object> as parameter.

Why can't I create an iterator for that map like that:
public static String[] getStrings(final Map<String, ? extends Object> p_tmInput)
{
    Iterator<Map.Entry<String, Object>> it = p_tmInput.entrySet().iterator();
    ...
}
Doing so my compiler says:

Type mismatch: cannot convert from Iterator<Map.Entry<String,capture#3-of ? extends Object>> to Iterator<Map.Entry<String,Object>>

My understanding of bounded wildcards in Java at the moment says that that map contains java.lang.String as keys and any objects deriveing from java.lang.Object.
I would be glad for any hint.


Thanks in advance and best regards,

Sebastian
  • 1. Re: Map iterator
    DrClap Expert
    Currently Being Moderated
    user13736071 wrote:
    My understanding of bounded wildcards in Java at the moment says that that map contains java.lang.String as keys and any objects deriveing from java.lang.Object.
    No, that's a standard misunderstanding. It means the map contains String as keys and objects of some particular type which is unknown at compile time. For example you might pass a Map<String, File> to that method in which case the type would be File and the objects would be File objects.

    And in that case your map entries would be MapEntry<String, File> objects. This is not a subtype of MapEntry<String, Object> and hence you can't use MapEntry<String, Object> in your iterator.

    Was there a particular reason you decided to use that wildcard in that method? And does that reason still apply now that you have read this?
  • 2. Re: Map iterator
    831231 Newbie
    Currently Being Moderated
    Hello DrClap,

    and thank you for the response. Actually that method is a legacy method that I wanted to make use of generics. Still I do not understand the reason for that compile error. What you are telling is for my understanding the unbounded wildcard operator (what in my case would be Map<String, ?>. In that specific case, the type is truely unknown at compile time. But with bounded wildcards it is somewhat different. If I may cite the generics tutorial from SUN:

    >
    4.1 Bounded Wildcards
    Consider a simple drawing application that can draw shapes such as rectangles and circles. To represent these shapes within the program, you could define a class hierarchy such as this:
    public abstract class Shape {
    public abstract void draw(Canvas c);
    }
    public class Circle extends Shape {
    private int x, y, radius;
    public void draw(Canvas c) { ... }
    }
    public class Rectangle extends Shape {
    private int x, y, width, height;
    public void draw(Canvas c) { ... }
    }
    These classes can be drawn on a canvas:
    public class Canvas {
    public void draw(Shape s) {
    s.draw(this);
    }
    }
    Any drawing will typically contain a number of shapes. Assuming that they are represented as a list, it would be convenient to have a method in Canvas that draws
    them all:
    public void drawAll(List<Shape> shapes) {
    for (Shape s: shapes) {
    s.draw(this);
    }
    }
    Now, the type rules say that drawAll() can only be called on lists of exactly Shape: it cannot, for instance, be called on a List<Circle>.
    That is unfortunate, since all the method does is read shapes from the list, so it could just as well be called on a List<Circle>. What we really want is for the method to accept a list of any kind of shape:
    public void drawAll(List<? extends Shape> shapes) { ... }
    There is a small but very important difference here: we have replaced the type List<Shape> with List<? extends Shape>. Now drawAll() will accept lists of any subclass of Shape, so we can now call it on a List<Circle> if we want. List<? extends Shape> is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape1. We say that Shape is the
    upper bound of the wildcard. 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.
    >

    So in my words I would conclude that cite like that.
    The method drawAll(List<? extends Shape> takes a List of any subtype of class Shape. So when iterating over that list I may refer to any element in that map as Shape. What I cannot do is add any type of Shape to the list, since I do not know the actual type of the list. But that still does not explain, why I can't have an Iterator<Entry<String, Object>> when my map is of type Map<String, ? extends Object>.

    Please correct me if I have concluded wrong.

    Regards,

    Sebastian
  • 3. Re: Map iterator
    802316 Pro
    Currently Being Moderated
    Generics are more perdantic than regular references. With references, Object means Object OR any subclass. With Generic <Object> means just Object, not its subclasses.

    BTW: It rare you need to use an Iterator these days. and your IDE is you firend for creating for-each loop. e.g. in my IDe i just type `iter`+<tab> and it give me the option of iterating over my entrySet and get the types right for me.
    for(Map.Entry<String, ? extends Object> entry: p_tmInput.entrySet()) {
  • 4. Re: Map iterator
    831231 Newbie
    Currently Being Moderated
    Hi Peter,

    you are right that I could use a for-each loop in that place. And in deed my IDE (eclipse) is my friend which told me to use an Iterator<?> what ends up in a compile error as well. Using an Iterator<Entry<String, ? extends Object> causes the same compile error as using Iterator<Entry<String, Object>>. By the way your code snippet does not compile either (same error).

    To make it clear: I do not search for a solution. Actually I solved this like that:
    Iterator<String> it = p_tmInput.keySet().iterator();
    while( it.hasNext() )
    {
        Object objValue = p_tmInput.get(it.next());
        ....
    I just want to understand what is the wrong idea behind my attempt. Pretty intellectual I guess :-)

    Regards,

    Sebastian
  • 5. Re: Map iterator
    802316 Pro
    Currently Being Moderated
    This code compiles and runs for me. Perhaps you have an old version of eclipse with a bug in it or you are not doing what you think you are doing because it should compile.
    Map<String, Object> map = new LinkedHashMap<String, Object>();
    map.put("hello", 1);
    map.put("world", 2);
    Map<String, ? extends Object> p_tmInput = map;
    
    for(Map.Entry<String, ? extends Object> entry: p_tmInput.entrySet()) { // compiles fine.
    
        System.out.println(entry.getKey()+": "+entry.getValue());
    }
    prints
    hello: 1
    world: 2
  • 6. Re: Map iterator
    831231 Newbie
    Currently Being Moderated
    You are right, my fault.

    Doing it within a for-each loop as you sugguested works fine. Seems than that this is some problem with Iterators. I will switch to using for-loops in such cases.
    Thank you all.

    Sincerely,

    Sebastian

Legend

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