views:

67

answers:

4

In Java, is it possible to associate some object (i.e. a String) with a function to be called ?

I have two similar menus and both have a onClickListener with some code like this:

if (item.id==0) {
 doSomeFunction(argument);
}
else if (item.id==1) {
 doSomeOtherFunction();
}
else if (item.id==2) {
 doStuff(arg2);
}
else {

}

I would just like to have an object (i.e. Hashtable) that would allow me to define associations between "item.id" -> "code to execute".

Example:

0 -> doSomeFunction(argument);
1 -> doSomeOtherFunction();
2 -> doStuff(arg2);

As a result I could skip these conditionals and just perform a function call of the function stored for a given item.id.

For example, with Lisp I could associate the item.id with a function and then use Lisp's "funcall" to call that function.

Is there a standard way to do something like this in Java?

Are there alternative solutions to skip the conditionals?

Please note that the called functions may need to receive parameters.

Thanks.

+2  A: 

Since it looks like your functions don't need to return any values, you could associate each ID key with a Runnable object that calls the method you want. For example:

Map<Integer, Runnable> map = ...;
map.put(0, new Runnable() {
  public void run() {
    doSomeFunction();
  }
});

map.get(0).run();

If you need the methods to be able to return a value, you can use Callable rather than Runnable. You can also create your own similar interface and use it instead if needed.

Currently in Java this sort of thing has to be done with an instance of a class with a single abstract method such as Runnable. Java 7 or 8 (depending on how they decide to proceed) will add lambda expressions and method references, which I think is what you really want. Method references would allow you to do something like this (using a Map<Integer, Runnable> just like above).

map.put(0, Foo#doSomeFunction());

Edit:

Your update indicates that some of these methods would need to have arguments passed to them. However, you seem to be asking for two different things. In your example of declaring the mapping

0 -> doSomeFunction(argument);
1 -> doSomeOtherFunction();
2 -> doStuff(arg2);

the arguments are specified, indicating that they are either values that are available at the time you declare the mapping or they're fields in the class or some such. In either case, you'd be able to declare them when you create the Runnable:

 new Runnable() {
   public void run() {
     doSomeFunction(argument);
   }
 }

Here, argument would either need to be a field or be declared final.

If, on the other hand, you don't have references to the arguments at the time you create the mapping, you'd have to create some consistent interface that all the method calls could share. For example, the least common interface that could work for your sample would be one that declared two parameters and a void return:

public interface CustomRunnable<T,G> {
  public void run(T arg1, G arg2);
}

Then you could have:

map.put(0, new CustomRunnable<Foo,Bar>() {
  public void run(Foo arg1, Bar arg2) {
    doSomeFunction(arg1);
  }
});
map.put(1, new CustomRunnable<Foo,Bar>() {
  public void run(Foo arg1, Bar arg2) {
    doSomeOtherFunction();
  }
});
map.put(2, new CustomRunnable<Foo,Bar>() {
  public void run(Foo arg1, Bar arg2) {
    doStuff(arg2);
  }
});

Each CustomRunnable only uses the arguments that it needs, if any. Then you could do the call like this:

map.get(item.id).run(argument, arg2);

Obviously, this is getting pretty ugly but it's pretty much what you'd have to do in that case.

ColinD
+1 for the interesting examples and for noting the possible Lambdas in Java 7/8.
MyNameIsZero
+1  A: 

Yes, there are several ways to do this. One example is to implement an interface anonymously and store it in a HashMap, something like this:

public class MyListeningClass
{
    ...

    public interface MyEventHandler
    {
        public void handle(String myParam);
    }

    private HashMap<ItemType, MyEventHandler>  eventHandlers= new HashMap<ItemType, MyEventHandler>();

    initItems()
    {
        //do for each item
        eventHandlers.put(item1, new MyEventHandler(){public void handle(String myParam){
                   //do whatever I want to
                 }});
    }

    myListeningMethod(ItemType item)
    {
        eventHandlers.get(item).handle("a parameter");
    }
}
Peter DeWeese
A: 

Java Swing way of answer for this is Action class. basically it follows command pattern...

Dhana
+1  A: 

The usual thing to do is to add a different action listener (command) on to each menu item, implemented as a short anonymous inner class. Unfortunately the syntax is currently verbose and it doesn't help start up time any, but it's still generally the best approach.

You don't say what library you are using. onClickListener isn't Swing, but if you were using Swing, you'd want JMenuItem.addActionListener.

Tom Hawtin - tackline
The Library I'm using is the one belonging to "Android" (mobile OS). Thanks.
MyNameIsZero