Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map of delegates in Java

I am trying to do a very simple command line library for interactive Java programs. You start the Java program and it prompts you for commands. The syntax is:

> action [object_1, [object_2, [... object_n] ... ]]

for example:

> addUser name "John Doe" age 42

Here action = "addUser", object_1 = "name", object_2 = "John Doe", object_3 = "age", object_4 = "42".

Everything after action is an object (that is, an action uses objects). You can see that the action and the object are simple strings. Object strings can also be converted to numbers if necessary.

My plan is that the user of this command line library would simply create methods (belonging to any suitable Java object) and assign each method to a specific action. The objects in the command line become parameters for the method the user assigns. A suitable class that implements a method for the example above would be:

class Foo {
    public void addUserHandler(
            String param1, String param2, String param3, Integer param4) {
        do.someThing();
    }
}

When a user types a command, the corresponding function assigned by the programmer gets called with the parameters specified in the command line.

I know that Java doesn't have function pointers (like C) or delegates (like C#) and the way to implement callbacks is through an interface, however, I don't see how can I use interfaces in this scenario. The problem I see with interfaces is that they have:

  1. A fixed number of functions to be implemented
  2. The functions to be implemented have a fixed declaration.

The problem with (1) is that the user of my library may decide to implement any number of functions, for as many actions as he wants to support. The problem with (2) is that the user would want to assign functions with descriptive names, like addUserHandler() for "addUSer" action.

If Java had delegates or function pointers I would just create a Map between Strings (representing actions) and delegates (for the actual implementation of the action in the program). I think I can emulate delegates with Reflection, but it is gory and I lose the type safety, as I'd have to use String names for classes and methods. Is there any better way?

Thanks,

like image 500
Aram Hăvărneanu Avatar asked Jan 23 '23 03:01

Aram Hăvărneanu


2 Answers

If you want the user to get automagic type translation (e.g. from strings to ints), then reflection is the only way. if you make the user do the work, then you don't need reflection (and you get type safety):

your command interface:

public interface MyCommand {
  public void execute(String[] args);
}

an implementation:

public class MyObj {
  public void doSomething(String param1, Integer param2) {
  }

  private void register() {
    mainApp.registerCommand("doSomething", new MyCommand() {
      @Override public void execute(String[] args) {
        doSomething(args[0], Integer.parseInt(args[1]));
      }});
  }
}
like image 123
james Avatar answered Jan 30 '23 04:01

james


Kudos to you for the sheer awesomeness of this question. You're pretty much up against the limits of what Java can do. (Though, of course, delegating like you describe would be a breeze in any functional language.)

First of all, limitation #2 should not be an issue. As of 1.5, Java no longer restricts you to fixed declarations in methods. You can use an ellipsis to indicate a variable number of arguments, like so

public static double average( double... numbers ) {
    double total = 0.0;
    for (double d : numbers) {
        total += d;
    }
    return total / numbers.length;
}

I'm not entirely sure how to get around limitation #1, but I'm thinking about something with generics and/or anonymous inner classes. I'm guessing here -- I haven't tried doing this myself -- but you could create a map of function names and delegate classes like so:

public interface Callback {...}

public interface AddUserCallBack extends Callback {...}

public class UserImpl<T extends Callback> {
    public T getDelegateRoutine();
    ... 
}

Generics in Java have some hair-pulling frustrations associated with them, primarily due to type erasure. You may need to juggle both interfaces and abstract base classes before you get it to do what you want. But something like this should work for you.

like image 31
rtperson Avatar answered Jan 30 '23 04:01

rtperson