How to work with varargs and reflection
Simple question, how make this code working ?
public class T {
public static void main(String[] args) throws Exception {
new T().m();
}
public // as mentioned by Bozho
void foo(String... s) {
System.err.println(s[0]);
}
void m() throws Exception {
String[] a = new String[]{"hello", "kitty"};
System.err.println(a.getClass());
Method m = getClass().getMethod("foo", a.getClass());
m.invoke(this, (Object[]) a);
}
}
Output:
class [Ljava.lang.String;
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:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
Test.class.getDeclaredMethod("foo", String[].class);
works. The problem is that getMethod(..)
only searches the public
methods. From the javadoc:
Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.
Update: After successfully getting the method, you can invoke it using:
m.invoke(this, new Object[] {new String[] {"a", "s", "d"}});
that is - create a new Object
array with one element - the String
array. With your variable names it would look like:
m.invoke(this, new Object[] {a});
//prior to edit:
Your problem is the fact that getMethod
looks for a public
member.
From the Class.getMethod
(emphasis mine):
Returns a
Method
object that reflects the specified public member method of the class or interface represented by this Class object
So you have two options:
- Make
public void foo(String... s)
and usegetMethod
- Use
getDeclaredMethod
instead
Note that the same difference exists for getField/s
vs getDeclaredField/s
and getConstructor/s
vs getDeclaredConstructor/s
.
//invoke
problem
This is particularly nasty, but what happens is that invoke(Object obj, Object... args)
makes it tricky if you need to pass an array of reference type as an only argument, because it is cast-able to Object[]
, even though it should be wrapped inside a new Object[1]
instead.
You can do:
m.invoke(this, new Object[] {a}); // Bohzo's solution
This bypasses the vararg mechanism. More succinctly you can also do:
m.invoke(this, (Object) a);
The cast to Object
makes the vararg mechanism do the work of creating the array for you.
The trick is also needed when passing a null
as an argument to varargs, and has nothing to do with reflection.
public void foo(String... ss) {
System.out.println(ss[0]);
}
foo(null); // causes NullPointerException
foo((String) null); // prints "null"