views:

329

answers:

2

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)
+5  A: 
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});
Bozho
Thanks! But now i get stucked at the invocation.
PeterMmm
Thanks again! Didn't see that.
PeterMmm
+1 for `invoke` solution; that's a nasty one.
polygenelubricants
@PeterMmm good. btw, it is an accepted practice to upvote useful (inc. the accepted) answers. You have only 2 upvotes, which is contrary to the idea of SO.
Bozho
You can also do `m.invoke(this, (Object) a);` (see my solution).
polygenelubricants
+3  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 use getMethod
  • 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"
polygenelubricants
+1, nice for this cast thing
PeterMmm