views:

185

answers:

6

Currently, I have a bunch of Java classes that implement a Processor interface, meaning they all have a processRequest(String key) method. The idea is that each class has a few (say, <10) member Strings, and each of those maps to a method in that class via the processRequest method, like so:

class FooProcessor implements Processor
{
    String key1 = "abc";
    String key2 = "def";
    String key3 = "ghi";
    // and so on...

    String processRequest(String key)
    {
        String toReturn = null;
        if (key1.equals(key)) toReturn = method1();
        else if (key2.equals(key)) toReturn = method2();
        else if (key3.equals(key)) toReturn = method3();
        // and so on...

        return toReturn;
    }

    String method1() { // do stuff }
    String method2() { // do other stuff }
    String method3() { // do other other stuff }
    // and so on...
}

You get the idea.

This was working fine for me, but now I need a runtime-accessible mapping from key to function; not every function actually returns a String (some return void) and I need to dynamically access the return type (using reflection) of each function in each class that there's a key for. I already have a manager that knows about all the keys, but not the mapping from key to function.

My first instinct was to replace this mapping using if-else statements with a Map<String, Function>, like I could do in Javascript. But, Java doesn't support first-class functions so I'm out of luck there. I could probably dig up a third-party library that lets me work with first-class functions, but I haven't seen any yet, and I doubt that I need an entire new library.

I also thought of putting these String keys into an array and using reflection to invoke the methods by name, but I see two downsides to this method:

  • My keys would have to be named the same as the method - or be named in a particular, consistent way so that it's easy to map them to the method name.
  • This seems WAY slower than the if-else statements I have right now. Efficiency is something of a concern because these methods will tend to get called pretty frequently, and I want to minimize unnecessary overhead.

TL; DR: I'm looking for a clean, minimal-overhead way to map a String to some sort of a Function object that I can invoke and call (something like) getReturnType() on. I don't especially mind using a 3rd-party library if it really fits my needs. I also don't mind using reflection, though I would strongly prefer to avoid using reflection every single time I do a method lookup - maybe using some caching strategy that combines the Map with reflection.

Thoughts on a good way to get what I want? Cheers!

A: 

As you've noticed, you can do what you want using the Reflection API, but you loose some benefits of the Java compiler, on top of the issues you've already come up with. Would wrapping your Strings in an object and using the Visitor pattern solve your issue? Each StringWrapper would only accept a Visitor that has the right method, or something along those lines.

justkt
I don't think the Visitor pattern is the right approach here - that is, I'm not sold on it. What would the `Visitor` operate on? What would the operation be?
Matt Ball
@Bears will eat you - As I was thinking of this idea, I pictured the Visitor visiting a wrapper for each String, but then again it looks like you don't know what String will appear when. I'm thinking the Interface idea is much simpler.
justkt
Visitor? Use either command or strategy pattern. The other answers demonstrates the command pattern. You can find real life examples of patterns [here](http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns/2707195#2707195) so that you can learn a bit more about them.
BalusC
+3  A: 

There aren't any first-class standalone functions, but you can do what you want with an interface. Create an interface that represents your function. For example, you might have the following:

public interface ComputeString
{
    public String invoke();
}

Then you can create a Map<String,ComputeString> object like you want in the first place. Using a map will be much faster than reflection and will also give more type-safety, so I would advise the above.

Michael Aaron Safyan
That would mean having an implementing class for every single function I want to invoke, which seems clunky and requires a bit of boilerplate for every single function. Doesn't sound that awesome to me.
Matt Ball
It's totally clunky, but that's Java's fault for not directly supporting higher-order functions.
Chris Conway
@Bears, yeah it is clunky, but you can construct anonymous classes, which should make it somewhat less clunky, like map.put(str,new ComputeString(){public String invoke(){ return "blah"; }});
Michael Aaron Safyan
+1  A: 

While you can't have first class functions, there are anonymous classes which can be based on an interface:

interface ProcessingMethod {
   String method();
}

Map<String, ProcessingMethod> methodMap = new HashMap<String, ProcessingMethod>();
methodMap.put("abc", new ProcessingMethod() {
   String method() { return "xyz" }
});
methodMap.put("def", new ProcessingMethod() {
   String method() { return "uvw" }
});

methodMap.get("abc").method();

Or you could use Scala :-)

RoToRa
A: 

Use a Map where the key is a string and the value is an object that implements an interface containing method(). That way you can get the object containing the method you want out of the map. Then just call that method on the object. For example:

class FooProcessor implements Processor{

    Map<String, FooMethod> myMap;

    String processRequest(String key){
        FooMethod aMethod = myMap.getValue(key);
        return aMethod.method();
    }        
}
mjh2007
A: 

This example uses an enum of named functions and an abstract FunctionAdapter to invoke functions with a variable number of homogeneous parameters without reflection. The lookup() function simply uses Enum.valueOf, but a Map might be worth it for a large number of functions.

trashgod
A: 

What about Method class from the reflection API? You can find methods of a class based on name, parameters, or return type. Then you just call Method.invoke(this, parameters).

That's pretty much the same as a Map from JavaScript you are talking about.

tulskiy