Java Reflection Call Error java.lang.IllegalArgumentException: wrong number of arguments

Keywords: Programming Java

Problem Description

The class Target.java has an execute() method that takes a String array as a parameter

public class Target {
    public void execute(String[] args) {
        System.out.println("call execute method with parameter type String[]");
    }
}

Call this method through reflection in the following way

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        String[] parameters = {"1"}; // parameter array
        Class targetClass = Class.forName("Target");// get target class "Target"
        Object instance= targetClass.newInstance();
        Method execute = targetClass.getDeclaredMethod("execute", String[].class);// get target method "execute"
        execute.invoke(instance, parameters);// invoke method
    }
}

Error in result console

Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at Test.main(Test.java:16)

problem analysis

Find the Method.invoke method, which actually receives a variable length parameter (Varargs).

public Object invoke(Object obj, Object... args)

When the compiler finds something similar

method.invoke(object, arg1, arg2)

When represented this way, an array is implicitly created, similar to the new Object [] {arg1, arg2}, and then used as an argument to the invoke method. But if the parameter of the target method is an array, such as

method.invoke(object, Object[])

The compiler will assume that you have put all the parameters in the array and will not wrap them again.In this case, what is passed as a parameter to the target method is actually the elements inside the array, not the array itself.

So let's look back and forth at the example above, in fact, the main method ultimately calls the execute method with a String type as a parameter through reflection, so let's do an experiment

public class Target {
    public void execute(String[] args) {
        System.out.println("call execute method with parameter type String[]");
    }

    public void execute(String arg) {
        System.out.println("call execute method with parameter type String");
    }
}

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        String[] parameters = {"1"}; // parameter array
        Class targetClass = Class.forName("Target");// get target class "Target"
        Object instance= targetClass.newInstance();
        Method execute = targetClass.getDeclaredMethod("execute", String.class);// get target method "execute"
        execute.invoke(instance, parameters);// invoke method
    }
}

The final print is (note that the two method parameters are different)

call execute method with parameter type String

Problem solving

In fact, the solution to this situation is simple. Just wrap the array as the first element of the Object array, and the compiler will pass the array itself as a parameter to the target method, as follows:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        String[] parameters = {"1"}; // parameter array
        Class targetClass = Class.forName("Target");// get target class "Target"
        Object instance = targetClass.newInstance();
        Method execute = targetClass.getDeclaredMethod("execute", String[].class);// get target method "execute"
        execute.invoke(instance, new Object[] {parameters});// invoke method
    }
}

summary

When calling a method with reflection, wrap the array in another Object array if the target method's input is an array.

Posted by TRemmie on Thu, 28 Nov 2019 21:51:17 -0800