2


I want to route certain chars to methods, so that when the char is typed in the command-line the method is then executed.

Based on the answer How to call a method stored in a HashMap, I'm mapping these chars to methods by using the "Command" design-pattern.

However I want to generically implement this, so it seems that I need to implement reflection in order to use the Method class as a parameter. My attempt is getting a NullPointerException on the field private Method method in my anonymous class...


Here is my code:
import java.lang.reflect.Method;

public interface InvokesMethod {

    public void invokeMethod() throws Exception;
    public void setMethod(Method method);
} // end of interface


import java.util.HashMap;
import java.lang.reflect.Method;

public class Terminal {

    public HashMap<Character, InvokesMethod> commands;

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

        try {
            this.setCommand('p',
                 this.getClass().getDeclaredMethod("printHelloWorld"));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void printHelloWorld() {
        System.out.println("Hello World!");
    }

    private void setCommand(char letter, Method method) {
        this.commands.put(letter, new InvokesMethod() {

            // NullPointerException starts here in the stack-trace:
            private Method method;

            @Override
            public void invokeMethod() throws Exception {
                method.invoke(null);
            }

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

    public void executeCommand(char letter) throws Exception {
        this.commands.get(letter).invokeMethod();
    }
} // end of class


public class Main() {

    public static void main(String[] args) {
        Terminal commandLine = new Terminal();

        try {
            commandLine.executeCommand('p');

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} // end of class
Community
  • 1
  • 1
Ian Campbell
  • 2,678
  • 10
  • 56
  • 104

2 Answers2

1

Regards to your code you didn't initiate method. Bear in mind that execute with null you must call public static method:

Your other issue , you didn't initiated interface properly. Here is working example:

InvokesMethodItf

public interface InvokesMethodItf {

public void invokeMethod() throws Exception;
public void setMethod(Method method);
} 

InvokesMethod

public class InvokesMethod implements InvokesMethodItf{

private Method method;

@Override
public void invokeMethod() throws Exception {
     method.invoke(null);
}

@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>();

    try {
        this.setCommand('p',
             this.getClass().getDeclaredMethod("printHelloWorld"));

    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void printHelloWorld() {// method.invoke(null) looking for "static" method
    System.out.println("Hello World!");
}


private void setCommand(char letter, Method method) {

    InvokesMethodItf inv = new InvokesMethod();

    inv.setMethod(method);

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

public void executeCommand(char letter) throws Exception {
    this.commands.get(letter).invokeMethod();
 }
}

Main

public class Main {
public static void main(String[] args) {
    Terminal commandLine = new Terminal();

    try {
        commandLine.executeCommand('p');

    } catch (Exception e) {
        e.printStackTrace();
    }
  }
}

Output:

Hello World!
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • Ok awesome, thanks for the help @Maxim! I solved this problem with your previous suggestion to pass the methods as Strings in the HashMap, which simplifies the code as well. I'm posting this possible solution here as an answer as well, cheers :) – Ian Campbell Sep 28 '13 at 05:29
  • 1
    I fixed your answer and it seems work, take a look posted above. I think its more clear – Maxim Shoustin Sep 28 '13 at 05:31
0


Thanks to @Maxim's original suggestion here, I have an alternate solution by setting the methods as Strings in the HashMap instead --

import java.util.HashMap;
import java.lang.reflect.Method;

public class Terminal {

    private HashMap<Character, String> commands;

    public Terminal() {
        this.commands = new HashMap<Character, String>();
        this.commands.put('p', "printHelloWorld");
    }

    private void printHelloWorld() {
        System.out.println("Hello World!");
    }

    public void executeCommand(char letter) throws Exception {
        Method method = getClass().getDeclaredMethod(this.commands.get(letter));
        method.invoke(this);
    }


public class Main {
    public static void main(String[] args) {
        Terminal commandLine = new Terminal();

        try {
            commandLine.executeCommand('p');

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} // end of class


Output:

Hello World!


Now to figure out how to pass parameters to the reflected methods...
Ian Campbell
  • 2,678
  • 10
  • 56
  • 104
  • Ok, so to use reflection to invoke a method with parameters, you have to pass each parameter's class as an argument to the `.getDeclaredMethod` following the first argument (which is the method's name as a String). For example, a String argument would then be passed as `String.class`. Then having a String parameter in my `.executeCommand` method will allow you to then pass it as an argument to `.invoke`, following the first argument (which is the object that the method is acting on). – Ian Campbell Oct 01 '13 at 03:22
  • Also, overloading of the `.executeCommand` method may be required, or it could use "varargs" instead (in which the class type would then be String[].class, because `String... varargs` is really just a String array). – Ian Campbell Oct 01 '13 at 03:23