問題描述
類Target.java有一個execute()方法,用一個String數組作爲參數
public class Target {
public void execute(String[] args) {
System.out.println("call execute method with parameter type String[]");
}
}
用如下方式,通過反射去調用這個方法
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
String[] parameters = {"1"}; // parameter array
Class targetClass = Class.forName("Target");// get target class "Target"
Object instance= targetClass.newInstance();
Method execute = targetClass.getDeclaredMethod("execute", String[].class);// get target method "execute"
execute.invoke(instance, parameters);// invoke method
}
}
結果控制檯出現報錯
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:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at Test.main(Test.java:16)
問題分析
找到Method.invoke方法,其實它接收的是一個變長參數(Varargs)。
public Object invoke(Object obj, Object... args)
當編譯器發現類似
method.invoke(object, arg1, arg2)
這樣的表示時,會隱式地創建一個數組,類似new Object [] {arg1, arg2},然後將該數組作爲invoke方法的參數。
但是如果目標方法的參數本來就是一個數組的時候,如
method.invoke(object, Object[])
編譯器會認爲你已經將所有的參數放到數組裏面了,從而不會再次包裝。於是在這種情況下,作爲參數傳遞給目標方法的,其實是數組裏面的元素,而不是數組本身。
所以我們來回看上面的例子,其實main方法最終通過反射嘗試調用的是,以一個String類型作爲參數的execute方法,我們來做個試驗
public class Target {
public void execute(String[] args) {
System.out.println("call execute method with parameter type String[]");
}
public void execute(String arg) {
System.out.println("call execute method with parameter type String");
}
}
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
String[] parameters = {"1"}; // parameter array
Class targetClass = Class.forName("Target");// get target class "Target"
Object instance= targetClass.newInstance();
Method execute = targetClass.getDeclaredMethod("execute", String.class);// get target method "execute"
execute.invoke(instance, parameters);// invoke method
}
}
最終打印出來的是(注意兩個方法參數是不一樣的)
call execute method with parameter type String
問題解決
其實這種情況的解決方法很簡單,只要把數組包裝成Object數組的第一個元素就好了,這樣編譯器就會把數組本身作爲參數,傳遞給目標方法了,如下:
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
String[] parameters = {"1"}; // parameter array
Class targetClass = Class.forName("Target");// get target class "Target"
Object instance = targetClass.newInstance();
Method execute = targetClass.getDeclaredMethod("execute", String[].class);// get target method "execute"
execute.invoke(instance, new Object[] {parameters});// invoke method
}
}
總結
當用反射調用方法時,如果目標方法的入參是一個數組,則要把數組包裝到另一個Object數組中。