views:

579

answers:

2

I am very new to processing.org and Java. I am trying to store objects in a HashMap, and then iterate over the values of the HashMap, calling methods on the stored objects. In order to do that, I assume I need to downcast the iterator to the type of my class, but this throws a ClassCastException ("java.util.HashMap$ValueIterator cannot be cast to sketch_oct27a$MyClass"). The following simplified code demonstrates this behavior:

import java.util.*;

void setup() {
  HashMap m = new HashMap();

  m.put("First", new MyClass());
  m.put("Second", new MyClass());
  m.put("Third", new MyClass());

  Iterator iter = m.values().iterator();

  while (iter.hasNext()) {
   ((MyClass)iter).SaySomething(); // Throws ClassCastException
   iter.next();
  }    
}

class MyClass { 
  void SaySomething() {
    println("Something");
  }
}

How do I call the SaySomething() method through the iterator?

+5  A: 

This is how you use an iterator:

((MyClass)iter.next()).SaySomething();

Better yet, use Generics so that you don't have to cast at all:

HashMap<MyClass> m = new HashMap<MyClass>();
...
Iterator<MyClass> iter = m.values().iterator();
...
iter.next().SaySomething();

You can then even skip the iterator entirely (actually, this syntax just hides it, it is still used implicitly):

for(MyClass element : m.values())
{
    element.SaySomething();
}
Michael Borgwardt
Or the posh for loop: `for (MyClass value : map.values()) { m.saySomething(); }`
Tom Hawtin - tackline
ah, right, of course
Michael Borgwardt
Thanks a lot for the swift response -- my problem is now fixed. As a side-note, processing.org doesn't do generics, as it's restricted to Java 1.4 (When using the PDE, that is).
Shteinitz
+2  A: 

Your code currently does this:

    ((MyClass)iter).SaySomething();

This says "cast iter as an instance of MyClass and call its SaySomething() method. This fails because the actual class of the iter instance will be some inner class that implements the interface java.util.Iterator. That class will not be a subclass of MyClass.

What you need to do is to get the iterator to deliver its next value, and cast that value; i.e.

    ((MyClass) (iter.next())).SaySomething();

which you can simplify to:

    ((MyClass) iter.next()).SaySomething();

because of Java operator precedence.

@Michael points out that if you use generics you can get rid of the overt typecast. You can simplify even further by using the "new" for loop syntax introduced in Java 5.0:

    HashMap<String, MyClass> m = new HashMap<String, MyClass>();

    m.put("First", new MyClass());
    m.put("Second", new MyClass());
    m.put("Third", new MyClass());

    for (MyClass mc : m.values()) {
        mc.SaySomething(); 
    }

In reality, the for loop is just syntactic sugar. Under the covers, the Iterator instance is being created and used as per your code (with the correction). Even the type cast is being performed ... unless the JIT can figure out that it can optimize it away.

EDIT: if you cannot use Java 1.5 then you are stuck with doing this the old-fashioned way. Sounds like "processing.org" needs to get their act together. The Java 1.4.x platform is really out of date.

Stephen C