views:

293

answers:

5

If I have function names stored as strings in a Hashtable.
Is there a way to access the functions via the stored strings?

EDIT I'm afraid the platform that i'm working on CLDC1.1/MIDP2.0 does not support Reflection.
Any workaround possible?

+2  A: 

Given a function name, you could use reflection to access the method. Besides the name of the function, you need to know its class, and have (or create via reflection if it has a noarg constructor or you know what parameters to pass to the constructor) an instance (if the method is not static) and you have to know the parameters required to pass to it.

Another option is to use the Method class as a pointer to the function. It has the advantage of knowing its class, and knowing its parameter requirements. It has the disadvantage of not being serializable.

EDIT: There is no way to access a method in Java without reflection by just having its name as a string. If you want an alternative pointer to a method, you could use an anonymous inner class that invokes the method you want that implements a known interface and pass that to your map. That would not be appropriate as the key of the map, but it would work as a value in the map.

Yishai
Is there another way?.. one without using reflection?
Kevin Boyd
+2  A: 

I haven't tried reflection on javame, but on javase you can use reflection to dynamically call a method.

The following snippet is taken from: http://java.sun.com/developer/technicalArticles/ALT/Reflection/

import java.lang.reflect.*;

public class method2 { public int add(int a, int b) { return a + b; }

  public static void main(String args[])
  {
     try {
       Class cls = Class.forName("method2");
       Class partypes[] = new Class[2];
        partypes[0] = Integer.TYPE;
        partypes[1] = Integer.TYPE;
        Method meth = cls.getMethod(
          "add", partypes);
        method2 methobj = new method2();
        Object arglist[] = new Object[2];
        arglist[0] = new Integer(37);
        arglist[1] = new Integer(47);
        Object retobj 
          = meth.invoke(methobj, arglist);
        Integer retval = (Integer)retobj;
        System.out.println(retval.intValue());
     }
     catch (Throwable e) {
        System.err.println(e);
     }
  }

}

EDIT: Rather than using if..then..else you may want to actually pass back a number, and use a switch, as it will be easier to read, IMO.

But, regardless, that is probably your only option given the limitations of JavaME.

James Black
With Java5 and the VarArgs and autoboxing, that code can be cleaned up a lot.
Yishai
@ James, could this be done without reflection?
Kevin Boyd
@Kevin Boyd, I don't know of a way to do it without reflection, as I can't even think of a way to do this with AOP. If you need that capability you may want to look at Groovy. I will think about it while I am at the gym. <g>@Yishai, the code can be cleaned up, I just thought that showing some example of using reflection may be useful, and I don't believe JavaME supports generics
James Black
+3  A: 

Just use a big long list of else-ifs:

[...]
} else if ("foo".equals(function)) {
    target. foo();
} else if ("bar".equals(function)) {
    target. bar();
[...]

(Although I generally don't like attempting vertical alignments in source, I think in cases like this it is well worth it.)

Storing a functor in the map is an alternative, bu might increase object size too much for many MIDP applications.

Tom Hawtin - tackline
The thought of that makes me grateful I don't develop JavaME, at least not on such a limited platform.
Yishai
@Tom:Point regarding memory makes lot of sense! Regarding speed, What would be faster to look up the HashTable method or the if/else-if.
Kevin Boyd
You would think `Hashtable` would be fast for all but the smallest *n*. However, I think the else-if chain should work out respectably fast. (It's slightly implementation dependent. `String.equals` could use the hash to discard almost all non-equal strings straight off.)
Tom Hawtin - tackline
+4  A: 

Class.forName() and newInstance() should be there on MIDP 1.1 and might be useful. Since you don't have full reflection, you could create a class that wraps all the function calls and invokes them. This assumes you know all the function calls in advance.

You'll need references to all the objects unless you're doing static calls. Kinda messy, but would get the job done.

public Object invoke(String name, Object[] args) {
   Object f = functionMap.get(name);
   if("f1".equals(name)) {
       return ((SomeInterface1)f).someFunction1((SomeArg0) args[0]), ...);
   } else if ("f2".equals(name)) {
       return ((SomeInterface2)f).someFunction2((SomeArg0) args[0]), ...);
   }...{
   } else if ("fN".equals(name)) {
       return ((SomeInterfaceN)f).someFunctionN((SomeArg0) args[0]), ...);
   }
}
Wayne Young
+1  A: 

I deal with similar issues in Hecl, an interpreter that runs on J2ME. It sort of depends on how many strings you want to deal with, but one method that works is to use the hash to look up an integer, and then use that in a switch statement, instead of the big list of if/then statements. By the way, you're welcome to use the Hecl source code; it's available under the liberal Apache license.

David N. Welton
I was looking in to Hecl yesterday! How strong are its UI capabilities?
Kevin Boyd
It can do anything that J2ME can do. Someone also contributed a MWT port.You probably don't want to use it for canvas work that has to be *fast*, as it's interpreted, but for basic applications, it does just fine!
David N. Welton