2011-01-22 582 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)); 

    } 
} 

}