Java反射調用報錯 java.lang.IllegalArgumentException: wrong number of arguments

問題描述

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數組中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章