2011-01-22 583 views
47

我想调用一个私有静态方法。我有它的名字。我听说可以使用Java反射机制来完成。我该怎么做?如何使用反射(Java)调用私有静态方法?

编辑:我试图调用该方法时遇到的一个问题是如何指定其参数的类型。我的方法接收一个参数,它的类型是Map。因此我不能这样做Map<User, String>.TYPE(在运行时,没有像Java类型擦除那样的Map之类的东西)。有另一种方法来获得该方法吗?

+1

尝试`MyClass.class.getDeclaredMethod( “myMethod的”,Map.class);`你的情况 – Cratylus 2011-01-22 21:05:59

+0

有趣的是通用该方法的信息在运行时仍然可用。 `Method.getGenericParameterTypes` – 2011-01-23 11:11:35

回答

79

假设你想调用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 
+0

谢谢。我的方法接收一个参数,它的类型是Map 。所以我不能做'Map .TYPE'。有另一种方法来获得该方法吗? – snakile 2011-01-22 20:49:57

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 

有许多检查的异常可能抛出的。参数类型和参数都是椭圆参数(可变长度),根据需要进行填充。规范中的JVM具有强类型的调用约定,因此您需要知道参数类型。所以,除非你正在编写某种应用程序容器,服务器组件容器,类似RMI的系统或基于JVM的语言,否则你应该避免这样做。从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(); 
    } 
    } 
} 
9

调用主不,你不能说Map<K,V>.class。这是因为类型擦除。在运行时,没有这样的事情。

幸运的是,你可以说只是普通的旧Map.class。运行时都是一样的。

如果警告打扰您,请搜索与泛型和类型擦除有关的其他问题,这里有关于此主题的大量信息。

1

我使用封装获取目标方法然后调用它的单个方法。当然可能有一些限制。这里是放入类中的方法和它的JUnit测试:

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)); 

    } 
} 

}