web-dev-qa-db-ja.com

可変引数とリフレクションの操作方法

簡単な質問ですが、このコードをどのように機能させるのですか?

public class T {

    public static void main(String[] args) throws Exception {
        new T().m();
    }

    public // as mentioned by Bozho
    void foo(String... s) {
        System.err.println(s[0]);
    }

    void m() throws Exception {
        String[] a = new String[]{"hello", "kitty"};
        System.err.println(a.getClass());
        Method m = getClass().getMethod("foo", a.getClass());
        m.invoke(this, (Object[]) a);
    }
}

出力:

class [Ljava.lang.String;
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:39)
        at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
        at Java.lang.reflect.Method.invoke(Method.Java:597)
25
PeterMmm
_Test.class.getDeclaredMethod("foo", String[].class);
_

動作します。問題は、getMethod(..)publicメソッドのみを検索することです。 javadocから:

このClassオブジェクトによって表されるクラスまたはインターフェイスの指定されたパブリックメンバーメソッドを反映するMethodオブジェクトを返します。

pdate:メソッドを正常に取得したら、次を使用して呼び出すことができます。

_m.invoke(this, new Object[] {new String[] {"a", "s", "d"}});
_

つまり、-1つの要素を持つ新しいObject配列を作成します-String配列。変数名を使用すると、次のようになります。

_m.invoke(this, new Object[] {a});
_
48
Bozho

//編集する前に:

あなたの問題は、getMethodpublicメンバーを探すという事実です。

_Class.getMethod_ (私の強調)から:

このClassオブジェクトによって表されるクラスまたはインターフェイスの指定されたpublicメンバーメソッドを反映するMethodオブジェクトを返します

したがって、2つのオプションがあります。

  • public void foo(String... s)を作成し、getMethodを使用します
  • 代わりにgetDeclaredMethodを使用してください

_getField/s_と_getDeclaredField/s_および_getConstructor/s_と_getDeclaredConstructor/s_にも同じ違いがあることに注意してください。


// invoke問題

これは特に厄介ですが、invoke(Object obj, Object... args)は_Object[]_にキャスト可能であるため、参照型の配列を唯一の引数として渡す必要がある場合は注意が必要です。代わりに、_new Object[1]_でラップする必要があります。

できるよ:

_m.invoke(this, new Object[] {a}); // Bohzo's solution
_

これはvarargメカニズムをバイパスします。より簡潔に行うこともできます。

_m.invoke(this, (Object) a);
_

Objectにキャストすると、varargメカニズムが配列を作成する作業を実行します。

このトリックは、引数としてnullをvarargsに渡すときにも必要であり、リフレクションとは関係ありません。

_public void foo(String... ss) {
    System.out.println(ss[0]);
}

    foo(null); // causes NullPointerException
    foo((String) null); // prints "null"
_
11