stav
stav

Reputation: 199

invoking method that takes string array as parameter results in IllegalArgumentException

I'm using runtime reflection to load a class which contains the following two methods:

public static void foo(int[] args)
{
    System.out.print("foo invoked: ");
    for(int arg : args)
        System.out.print(arg + " ");
    
    System.out.println();
}

public static void bar(String[] args)
{
    System.out.print("bar invoked: ");
    for(String arg : args)
        System.out.print(arg + " ");
    
    System.out.println();
}

(identical methods except one takes an int array and the other takes a string array)

I then attempt to invoke the two methods like so:

    int[] intArr = {1,2,3};
    clazz.getMethod("foo", int[].class).invoke(null, intArr);

    String[] strArr = {"1","2","3"};
    clazz.getMethod("bar", String[].class).invoke(null, strArr); //Exception

(here 'clazz' is the class the two methods reside in, which i have loaded at runtime)

The first invocation causes no exceptions and outputs the expected output, but the second invocation throws the following exception:

Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at ReflectionTests.main(ReflectionTests.java:63)

Why is this?

Upvotes: 3

Views: 663

Answers (2)

Sweeper
Sweeper

Reputation: 273258

Observe that invoke has this signature:

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

args is a variable-arity parameter to allow you to pass each parameter of the invoked method as a parameter of invoke, e.g.

someStaticMethod.invoke(null, param1, param2, param3);

would call:

someStaticMethod(param1, param2, param3);

On the other hand, the type of args is really just Object[], and there exists a conversion from String[] to Object[], since arrays are covariant.

So when you pass your String[] of strArr to invoke, one of two things could happen:

  • strArr is converted to an Object[], and each String is treated as a parameter to the method to be invoked.
  • the whole strArr is treated as one of the parameter of the method to be invoked, just like a non-array type would.

The compiler just so happens to prefer the first one (see here. Note how variable-arity invocation has the lowest priority), so you are actually passing the parameters "1", "2", "3" to the method, rather than one single String[].

On the other hand, int[] can't be converted to Object[] directly, because int is primitive, so the compiler can only choose the second option above.

One way to force the second option is to cast to Object:

clazz.getMethod("bar", String[].class).invoke(null, (Object)strArr);

Another way is to create another Object[] wrapping the String[]:

clazz.getMethod("bar", String[].class).invoke(null, new Object [] { strArr });

Upvotes: 7

You have to pass as first parameter of the invoke() method the initialized object that is going to execute that method.

Foo foo = new Foo(); Method method = clazz.getMethod("method");

method.invoke(foo, params[ ]);

The invoke() needs the object what is going to execute the method.

Upvotes: -1

Related Questions