1

I am following this question How to generically implement calling methods stored in a HashMap? I am trying to pass parameters while calling executeCommand function Example code is as follows, InvokesMethodItf

public interface InvokesMethodItf {

public void invokeMethod(String data) throws Exception; //pass the data as parameter
public void setMethod(Method method);
} 

InvokesMethod

public class InvokesMethod implements InvokesMethodItf{

private Method method;

@Override
public void invokeMethod(String data) throws Exception {
     method.invoke(data);    //pass the data to invoke (I think my problem is here). I dont know how to pass it.
}

@Override
public void setMethod(Method method) {
    this.method = method;
}

}

Terminal

public class Terminal {

public HashMap<Character, InvokesMethodItf> commands;

public Terminal() {
    this.commands = new HashMap<Character, InvokesMethodItf>();


}


private void setCommand(char letter, Method method) {

    InvokesMethodItf inv = new InvokesMethod();

    inv.setMethod(method);

    this.commands.put(letter, inv);
}

public void executeCommand(char letter, String data) throws Exception {
    this.commands.get(letter).invokeMethod(data);    //pass data to invoke method
 }
}

Main

    public class Main {           

    Terminal commandLine = new Terminal();
    commandLine.setCommand('h',test());   //This should give syntax error or i am not sure
    commandLine.executeCommand('h', "This is a test");

   public Method test(String data){
        Log.d("Test", data);

        return null;
    }
}

UPDATE: I am trying to set multiple methods using setCommand and execute it.

commandline.setCommand('p',this.getClass().getDeclaredMethod("parseData",String.class,Integer.class), this);
commandline.setCommand('p', this.getClass().getDeclaredMethod("test"), this);

Then , calling

commandline.executeCommand('p', "test", 2345);

Only test function is calling.(Last setCommand function is running). I think it is overwriting Method . Isn't there is someway to pass multiple methods in setCommand function. Changing type of Method to Method[] is not working either.

Community
  • 1
  • 1
NeiL
  • 791
  • 8
  • 35
  • When invoking a method, you also need to pass the instance holding the method you want to invoke: `method.invoke(instance, params)`. See the [API](http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object,%20java.lang.Object...)). – sp00m Sep 29 '14 at 09:08
  • For static method use `null` as instance – talex Sep 29 '14 at 09:11
  • @talex test is not a static method – Kumar Abhinav Sep 29 '14 at 09:19
  • @sp00m can you give more insight about the implementation? – NeiL Sep 29 '14 at 10:13

2 Answers2

1

When invoking a method, you need to pass the instance from which the method should be invoked as well as the parameters:

public interface MethodInvoker {

    // the method + the instance from which the method should be called
    public void setMethod(Method method, Object instance);

    // invokes the method
    public void invoke(Object... params) throws Exception;

}

Here an implementation:

public class MethodInvokerImpl implements MethodInvoker {

    private Method method;
    private Object instance;

    @Override
    public void setMethod(Method method, Object instance) {
        this.method = method;
        this.instance = instance;
    }

    @Override
    public void invoke(Object... params) throws Exception {
        // first param: instance, then the parameters
        method.invoke(instance, params);
    }

}

Then your Terminal:

public class Terminal {

    public Map<Character, MethodInvoker> commands;

    public Terminal() {
        commands = new HashMap<Character, MethodInvoker>();
    }

    // instance needed, since MethodInvoker#setMethod needs it
    public void addCommand(char letter, Method method, Object instance) {
        MethodInvoker invoker = new MethodInvokerImpl();
        invoker.setMethod(method, instance);
        commands.put(letter, invoker);
    }

    public void executeCommand(char letter, Object... params) throws Exception {
        commands.get(letter).invoke(params);
    }

}

Finally in your main:

public void test(String data) {
    System.out.println(data);
}

public void main() throws Exception {
    Terminal commandLine = new Terminal();
    // #text() will be called from "this" instance
    commandLine.addCommand('h', getClass().getMethod("test", String.class), this);
    commandLine.executeCommand('h', "This is a test");
}

Note that static methods don't need instances since they belong to the class, e.g.:

public void main() throws Exception {
    Terminal commandLine = new Terminal();
    // simply pass "null" as instance
    commandLine.addCommand('h', getClass().getMethod("test", String.class), null);
    commandLine.executeCommand('h', "This is a test");
}

public static void test(String data) {
    System.out.println(data);
}

See https://stackoverflow.com/a/542122/1225328 and https://stackoverflow.com/a/1348228/1225328 as well.

Community
  • 1
  • 1
sp00m
  • 47,968
  • 31
  • 142
  • 252
  • Thanx , i have one small doubt in getClass().getMethod("test", String.class) . Suppose , in my main activity i declare the test function normally . If i use getClass().getMethod("test", String.class) in some other scope,lets say i created the interface and use addCommand in that override function. It is catching this error java.lang.NoSuchMethodException: test [class java.lang.String] – NeiL Sep 30 '14 at 05:47
  • i did some debugging turns out getClass().getMethod("test", String.class) .checks the function in same scope(not global). – NeiL Sep 30 '14 at 06:47
  • 1
    @Neil `getClass()` returns the class of `this`. In my example, if `main()` is in class `MyMain` for example, `getClass()` will equal `MyMain.class` (unless it has been instantiated via a subclass). That is to say, if you want to call the method `AnyClass#anyMethod(String, Integer)`, then use `AnyClass.class.getMethod("anymethod", String.class, Integer.class)`. – sp00m Sep 30 '14 at 07:10
0

Method is an Object which you will have to get using reflection.As I see the problems in your code,you cannot get Method Object using method invocation.Change this in your Main class.Check the javadoc here for getting Method Object from Class Object(for class Object you can use Class.forName(String class_binary_name) or Class literal

   public class Main {           

        Terminal commandLine = new Terminal();
        Method method = Main.class.getMethod("test",String.class);
        commandLine.setMethod('h',method); 
        commandLine.executeCommand('h', "This is a test");

        public void test(String data){
            Log.d("Test", data);

        }
    }

Also,change the return type of test class to void if you are not planning to return anything

Kumar Abhinav
  • 6,565
  • 2
  • 24
  • 35
  • I tried the same code. Nothing is getting print in log. App is running fine. 09-29 15:10:18.873: W/System.err(1818): java.lang.IllegalArgumentException: expected receiver of type com.example.hello.MainActivity, but got java.lang.String 09-29 15:10:18.883: W/System.err(1818): at java.lang.reflect.Method.invokeNative(Native Method) 09-29 15:10:18.903: W/System.err(1818): at java.lang.reflect.Method.invoke(Method.java:511) Getting these system errors. I think problem is in method.invoke() function. – NeiL Sep 29 '14 at 09:57