views:

482

answers:

7

In python, if I have a few functions that I would like to call based on an input, i can do this:

lookup = {'function1':function1, 'function2':function2, 'function3':function3}
lookup[input]()

That is I have a dictionary of function name mapped to the function, and call the function by a dictionary lookup.

How to do this in java?

+2  A: 

You could use a Map<String,Method> or Map<String,Callable> etc,and then use map.get("function1").invoke(...). But usually these kinds of problems are tackled more cleanly by using polymorphism instead of a lookup.

__roland__
Specifically it sounds like the Strategy pattern (http://en.wikipedia.org/wiki/Strategy_pattern#Java) could help here (not quite as short as the Python equivalent, but..).
Gadi
How to do this by polymorphism? The function that needed to be involved is based on the input from the client. Could you show me? Thank you.
i-freaker
+13  A: 

Java doesn't have first-class methods, so the command pattern is your friend...

disclamer: code not tested!

public interface Command 
{
    void invoke();
}

Map<String, Command> commands = new HashMap<String, Command>();
commands.put("function1", new Command() 
{
    public void invoke() { System.out.println("hello world"); }
});

commands.get("function1").invoke();
Dan Vinton
Java does have closures, but not first-class methods. An anonymous inner class closes over (final) variables in its enclosing scope.
Nat
@Nat - actually, I'd never thought of anonymous or inner classes as closures, but I guess that they are. Answer updated to remove that comment. Thanks!
Dan Vinton
+1  A: 

Polymorphic example..

public interface Animal {public void speak();};
public class Dog implements Animal {public void speak(){System.out.println("treat? treat? treat?");}}
public class Cat implements Animal {public void speak(){System.out.println("leave me alone");}}
public class Hamster implements Animal {public void speak(){System.out.println("I run, run, run, but never get anywhere");}}

Map<String,Animal> animals = new HashMap<String,Animal>();
animals.put("dog",new Dog());
animals.put("cat",new Cat());
animals.put("hamster",new Hamster());
for(Animal animal : animals){animal.speak();}
rich
+2  A: 

There are several ways to approach this problem. Most of these were posted already:

  • Commands - Keep a bunch of objects that have an execute() or invoke() method in a map; lookup the command by name, then invoke the method.
  • Polymorphism - More generally than commands, you can invoke methods on any related set of objects.
  • Finally there is Reflection - You can use reflection to get references to java.lang.Method objects. For a set of known classes/methods, this works fairly well and there isn't too much overhead once you load the Method objects. You could use this to, for example, allow a user to type java code into a command line, which you execute in real time.

Personally I would use the Command approach. Commands combine well with Template Methods, allowing you to enforce certain patterns on all your command objects. Example:

public abstract class Command {
  public final Object execute(Map<String, Object> args) {
    // do permission checking here or transaction management
    Object retval = doExecute(args);
    // do logging, cleanup, caching, etc here
    return retval;
  }
  // subclasses override this to do the real work
  protected abstract Object doExecute(Map<String, Object> args);
}

I would resort to reflection only when you need to use this kind of mapping for classes whose design you don't control, and for which it's not practical to make commands. For example, you couldn't expose the Java API in a command-shell by making commands for each method.

Mr. Shiny and New
A: 

As mentioned in other questions, a Map<String,MyCommandType> with anonymous inner classes is one verbose way to do it.

A variation is to use enums in place of the anonymous inner classes. Each constant of the enum can implement/override methods of the enum or implemented interface, much the same as the anonymous inner class technique but with a little less mess. I believe Effective Java 2nd Ed deals with how to initialise a map of enums. To map from the enum name merely requires calling MyEnumType.valueOf(name).

Tom Hawtin - tackline
A: 

Unfortunately, Java does not have first-class functions, but consider the following interface:

public interface F<A, B> {
  public B f(A a);
}

This models the type for functions from type A to type B, as first-class values that you can pass around. What you want is a Map<String, F<A, B>>.

Functional Java is a fairly complete library centered around first-class functions.

Apocalisp
A: 

As everyone else said, Java doesn't support functions as first-level objects. To achieve this, you use a Functor, which is a class that wraps a function. Steve Yegge has a nice rant about that.

To help you with this limitation, people write functor libraries: jga, Commons Functor

ykaganovich