68

I would like to invoke a private static method. I have its name. I've heard it can be done using Java reflection mechanism. How can I do it?

EDIT: One problem I encountered when trying to invoke the method is how to specify the type of its argument. My method receives one argument and its type is Map. Therefore I cannot do Map<User, String>.TYPE (In run time there's no such a thing as Map because of Java Type erasure). Is there another way to get the method?

snakile
  • 52,936
  • 62
  • 169
  • 241

5 Answers5

117

Let's say you want to call MyClass.myMethod(int x);

Method m = MyClass.class.getDeclaredMethod("myMethod", Integer.TYPE);
m.setAccessible(true); //if security settings allow this
Object o = m.invoke(null, 23); //use null if the method is static
Landei
  • 54,104
  • 13
  • 100
  • 195
  • Thanks. My method receives one parameter and its type is Map. Therefore I cannot do 'Map.TYPE'. Is there another way to get the method? – snakile Jan 22 '11 at 20:49
  • 6
    @snakile:try `MyClass.class.getDeclaredMethod("myMethod", Map.class);` for your case – Cratylus Jan 22 '11 at 21:05
  • How you can change with (class, string, etc. ).type ? –  May 06 '16 at 09:15
  • 1
    @delive Usually you can just use `YourClass.class` as argument, the `Integer.TYPE` is just a crutch for primitive types, which have no real class. – Landei May 06 '16 at 09:42
  • But then how would you get the return value of the method? Since it's static it won't be saved in `o`. – Celeritas Jul 15 '16 at 09:45
  • @Celeritas The return value should be in `o`, this is *not* the object you're calling it on (if you had an object, it needs to be the first argument of `invoke`). – Landei Jul 15 '16 at 10:10
  • @Landei then what method do you call on `o` to get the value? – Celeritas Jul 15 '16 at 21:14
  • @Celeritas It *is* the value, you just need to cast it. E.g. if your static method returns a `String`, you can cast `o` to a `String`. `Method.invoke` must be able to return values from all kinds of methods, so which return type should it use in its method declaration? It can't know the type, so it uses `Object` (as some other reflection stuff does as well). – Landei Jul 15 '16 at 21:26
10

Invoke main from reflection tutorial

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class InvokeMain {
    public static void main(String... args) {
    try {
        Class<?> c = Class.forName(args[0]);
        Class[] argTypes = new Class[] { String[].class };
        Method main = c.getDeclaredMethod("main", argTypes);
        String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
        System.out.format("invoking %s.main()%n", c.getName());
        main.invoke(null, (Object)mainArgs);

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    } catch (NoSuchMethodException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    } catch (InvocationTargetException x) {
        x.printStackTrace();
    }
    }
}
Cratylus
  • 52,998
  • 69
  • 209
  • 339
2

No, you can't say Map<K,V>.class. This is because of type erasure. At runtime, there's no such thing.

Luckily, you can say just plain old Map.class. It's all the same at runtime.

If the warnings bother you, search for other questions related to generics and type erasure, there's a wealth of information on the subject here.

bmargulies
  • 97,814
  • 39
  • 186
  • 310
1

I use a single method that encapsulates getting the target method and then invoking it. Probably has some limitations, of course. Here is the method put into a class and its JUnit test:

public class Invoker {
/**
 * Get method and invoke it.
 * 
 * @author jbetancourt
 * 
 * @param name of method
 * @param obj Object to invoke the method on
 * @param types parameter types of method
 * @param args to method invocation
 * @return return value
 * @throws Exception for unforseen stuff
 */
public static final <T> Object invokeMethod(final String name, final T obj,
  final Class<?>[] types, final Object... args) throws Exception {

    Method method = obj.getClass().getDeclaredMethod(name, types);
    method.setAccessible(true);
    return method.invoke(obj, args);
}

/**
 * Embedded JUnit tests.
 */
@RunWith(JUnit4.class)
public static class InvokerTest {
    /** */
    @Test
    public void testInvoke() throws Exception {
        class TestTarget {
            private String hello() {
                return "Hello world!";
            }
        }

        String actual = (String) Invoker.invokeMethod("hello",
                new TestTarget(), new Class<?>[] {});
        String expected = "Hello world!";
        assertThat(actual, is(expected));

    }
}

}

Josef.B
  • 942
  • 8
  • 16
0
Object insecure; //This needs to be an initialized reference

Class c = insecure.getClass();
Method m = c.getMethod(name, parameterTypes); //Fill the name and types in
m.setAccessible(true);
m.invoke( insecure, parameters ); //Fill in the parameters you would like

There are a number of checked exceptions which may be thrown. Both parameterTypes and parameters are ellipse arguments (variable length), fill them in as needed. The JVM by specification has a strongly typed calling convention so you need to know the parameter types.

With that said, unless you are writing some sort of application container, server component container, RMI-like system, or JVM based langauge you should avoid doing this.

Mark
  • 1,128
  • 13
  • 21