How to use reflection to invoke a method with primitive arguments?

Lindstorm :

I am trying to create a method invokeMethod(String methodName, Object...args) which invokes a method from a superclass on the current instance. I have tried the following implementation.

    public void invokeMethod(String methodName, Object...args) {
        //Array to hold the classes of the arguments
        Class<?>[] classes = new Class<?>[args.length]; 
        //Initialize each class in the array to the class of each argument 
        for(int i = 0; i < args.length; i++)
            classes[i] = args[i].getClass();
        try {
            //find the method
            Method m = this.getClass().getMethod(methodName, classes);
            //invoke the method
            m.invoke(this, args);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

The problem with this implementation is if I try to invoke a method with primitive parameters, I get a NoSuchMethodException because it is looking for a method with parameters whose type is the wrapper class equivalent.

For example, if I try to invoke a method with signature line(float, float, float, float) by trying invokeMethod("line", 50f, 50f, 50f, 50f), I get an exception similar to

java.lang.NoSuchMethodException: DynamicSketch.line(java.lang.Float, java.lang.Float, java.lang.Float, java.lang.Float)
at java.base/java.lang.Class.getMethod(Class.java:2109)
at DynamicSketch.invokeMethod(DynamicSketch.java:32)
at DynamicSketch.setup(DynamicSketch.java:19)
at processing.core.PApplet.handleDraw(PApplet.java:2412)
at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1557)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:316)

Is there anyway to make my invokeMethod method work with primitive arguments?

Edit: The solution here does not work because I do not know exactly what primitive types are in the signature when executing my method. I want to be able to execute methods like size(int, int) and line(float, float ,float ,float) using my method, and the solution in the link does not easily work with this. The only solution I see to this is define an if statement for every possible method in the superclass and then use the solution in the link, but I wanted a less tedious way.

Sweeper :

When arguments such as 10 or 10.0f are passed into the method, they are automatically wrapped, because the parameter type is Object....

Therefore, you need to check for these wrapper types and unwrap them. Your for loop can look like this:

for(int i = 0; i < args.length; i++) {
    if (args[i].getClass() == Integer.class) {
        classes[i] = int.class;
    } else if (args[i].getClass() == Float.class) {
        classes[i] = float.class;
    } else if (args[i].getClass() == Double.class) {
        classes[i] = double.class;
    } else {
        classes[i] = args[i].getClass();
    }
}

I have only added 3 cases here, you can add the other 5 yourself.

This means that you can't call methods with wrapper type arguments now. If you want to call those as well, you need to

  1. unwrap all the wrapper types
  2. try to find a method with those non-wrapper types
  3. If not found, wrap them up again
  4. find the a method using the wrapper types.

Edit:

As Holger suggested in the comments, you can also let the JVM find a suitable method for you, by using

new Statement(this, methodName, args).execute();

Docs for the Statement class.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=93651&siteId=1