Java反射配列パラメータタイプ

5385 ワード

問:次のプログラムセグメントの実行時にコメント行に問題はありませんか?どうして?
public class Test {
    public void func(String[] args) {
        System.out.println((args == null) ? "null" : args.length);
    }
    public static void main(String[] args) throws Exception {
        Test obj = new Test();
        Method m = obj.getClass().getMethod("func", String[].class);
        m.invoke(obj, new String[1]);  //1
        m.invoke(obj, new Object[]{new String[] {"a", "b"}}); //2
        m.invoke(obj, (Object) new String[] {"a", "b"}); //3
        m.invoke(obj, new String[] {"a"});  //4
        m.invoke(obj, new String[] {"a", "b"}); //5
        m.invoke(obj, new String[2]);  //6
    }
}

答え:上記のプログラムの実行結果は以下の通りです.
null
2
2
Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
    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:15)
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)
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:17)
  • 注釈1についてnew String[1]がinvokeメソッドの可変パラメータに渡されると実質的にnull、すなわちObjectに分解されるため、注釈1は反射に成功しnullとして印刷される.
  • 注釈2については注釈1と同様であるが,2でinvokeメソッドに伝達される可変パラメータは実質的にnewのObject配列であるが,ここでのObject配列はnew String[]{"a","b"}の要素が1つしかないため,注釈2は反射に成功しString配列のパラメータを受信した要素を受信し,2として印刷する.
  • アノテーション3の場合、強制的にObjectタイプに変換することは、invokeメソッドに渡される可変パラメータがパラメータであり、パラメータ値がnew String[]{"a","b"}であるため、反射に成功し、印刷値が2となる.
  • 注釈4にとってパラメータタイプの不一致は、new String[]{"a"}がinvokeメソッドに渡されると実質的に"a"の値を持つパラメータ、すなわちStringタイプに分解されるため、反射時にfuncメソッドに必要なのはString[]配列タイプであり、伝達されるのは文字列である.したがって、タイプ不一致異常が放出される(注釈1と比較してnullは任意のオブジェクトタイプの値であってもよいことに注意).
  • 注釈5と注釈6については同様であり,いずれもinvokeメソッドに渡すと2つのパラメータとなるが,反射funcには1つのパラメータが必要であるため,反射時に異常を放出し,パラメータ個数が不正であることを示唆する.
  • 上記の問題に続いて、呼び出し文がm.invoke(obj,new String[]{})であると仮定する.時運行の結果はどうなりますか?この場合もIllegalArgumentException:wrong number of argumentsとエラーが発生します.new String[]{}は長さ0の配列を初期化しただけなので、invokeが長くなるパラメータ解析に渡すとパラメータ個数が一致しないため異常が放出されます.

  • 問:次のプログラムセグメントの実行時にコメント行に問題はありませんか?どうして?
    public class Test {
        public void func(String key, String[] args) {
            System.out.println((args == null) ? "null" : args.length);
        }
        public static void main(String[] args) throws Exception {
            Test obj = new Test();
            Method m = obj.getClass().getMethod("func", String.class, String[].class);
            m.invoke(obj, new String(), new String[1]);  //1
            m.invoke(obj, new String(), new Object[]{new String[] {"a", "b"}}); //2
            m.invoke(obj, new String(), (Object) new String[] {"a", "b"}); //3
            m.invoke(obj, new String(), new String[] {"a"});  //4
            m.invoke(obj, new String(), new String[] {"a", "b"}); //5
            m.invoke(obj, new String(), new String[2]);  //6
        }
    }
    

    答え:上記のプログラムの実行結果は以下の通りです.
    1
    Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
        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:14)
    2
    1
    2
    2
    
  • アノテーション1ではinvokeの2番目のパラメータはObjectタイプのargs変長パラメータであるため、アノテーション1でinvokeに渡されるargsは2つの実パラメータであることが明らかになり、解析後のinvokeのargsの2番目のパラメータはString配列であるためfuncパラメータタイプに完全に合致し、印刷配列長を1として正常に実行することができる.
  • アノテーション2についてはアノテーション1がinvokeに渡されるのと同様であるが,funcに渡される2番目のパラメータはString配列ではなくObject配列となるため,タイプが一致せずパラメータタイプが不整合で異常を放出する.
  • 注釈3のinvoke変長パラメータ分割解析は同じであるが、渡されたパラメータはObjectに強く変換され、Objectは任意のオブジェクトタイプのベースクラスであり、被強転タイプは実質的にString配列であるため、実行時には影響がないため、印刷配列長は2となる.
  • アノテーション4,5,6とアノテーション1は全く同じであり,String配列の要素値が異なると理解できるだけである.
  • 上記の問題に続いて、呼び出し文がm.invoke(obj,new String(),new String[]{})であると仮定する.時運行の結果はどうなりますか?この場合、印刷配列の長さは0です.理由はコメント1に似ています.

  • したがって,反射パラメータが配列タイプである場合,上記の問題は実質的にJavaの変長パラメータ問題であるため,変長パラメータ解析問題に特に注意すべきであることがわかる.
    ここでは、Javaメソッドに1つのパラメータが配列である場合、反射する場合、当然のことながら配列を伝達することはできません.配列を伝達する場合、複数のパラメータとして扱われます.配列をObjectに変換する必要があります.そうすれば、1つのパラメータを表すことができます.具体的な原因はJavaが長くなるパラメータの特性原理です.