5

小さな ScriptEngine の開発の一環として、私は反射的に Java メソッドを呼び出します。スクリプト エンジンによる呼び出しにより、オブジェクトにメソッド名と引数の配列が与えられます。メソッドを呼び出すために、 Class.getMethod(name, argument types) への呼び出しで解決しようとしました。
ただし、これは、引数のクラスとメソッドによって期待されるクラスが同じ場合にのみ機能します。

Object o1 = new Object();
Object out = System.out;
//Works as System.out.println(Object) is defined
Method ms = out.getClass().getMethod("println",o1.getClass());
Object o2 = new Integer(4);
//Does not work as System.out.println(Integer) is not defined
Method mo = out.getClass().getMethod("println",o2.getClass());

正しいメソッドを取得する「簡単な」方法があるかどうか、可能であれば引数の型に最も近い方法があるかどうか、またはこれを自分で実装する必要があるかどうかを知りたいです。

最も近い適合は次のとおりです。

Object o1 = new Integer(1);
Object o2 = new String("");
getMethod(name, o1.getClass())//println(Object)
getMethod(name, o2.getClass())//println(String)  

更新:
必要なものを明確にするために: スクリプト エンジンは、自由時間に書いている小さなプロジェクトなので、従わなければならない厳格な規則はありません。したがって、Javaコンパイラがコンパイル時にオブジェクトの静的型ではなく動的型のみでメソッドを選択するのと同じ方法で、エンジンから呼び出されたメソッドを選択するとうまくいくと思いました.(オートボクシングの有無にかかわらず)
これは私が最初に望んだことですClass.getMethod() が解決します。ただし、Class.getMethod() には、メソッドが宣言する引数の型とまったく同じクラスが必要です。サブクラスを使用すると、no such method Exception が発生します。これは正当な理由で発生する可能性がありますが、どの引数タイプが適合するかを事前に知らないため、メソッドが役に立たなくなります。
別の方法は、Class.getMethods() を呼び出して、返された配列を反復処理し、適切なメソッドを見つけようとすることです。ただし、最初に見つけた「良い」方法を取りたくない場合、これは複雑になるため、少なくとも処理する既存のソリューションがあることを望みました。

  • 最も近い適合: arg.getClass() == サブクラスおよびメソッド m(Superclass)、m(Subclass) の場合、m(Subclass) を呼び出します
  • 可変引数: System.out.printf(String ,String...)

オートボクシングのサポートもいいでしょう。
呼び出しを解決できない場合、例外がスローされる可能性があります ( ma(String,Object), ma(Object, String), args= String,String)
(ここまで作成した場合は、時間を割いて読んでくれてありがとう:- )))

4

3 に答える 3

2

を使用することをお勧めしますgetMethods()。すべてのパブリックメソッドの配列を返します(Method[])。

ここで最も重要なことは、
クラスが同じパラメータタイプを持つ複数のパブリックメンバーメソッドを宣言している場合、それらはすべて返される配列に含まれている」ということです。

次に行う必要があるのは、この配列の結果を使用して、それらのいずれか(存在する場合)が最も一致するものを判別することです。最も近いものが何であるかは、要件と特定のアプリケーションに大きく依存するため、自分でコーディングすることは理にかなっています。


これを行う方法の1つのアプローチを示すサンプルコード:

public Method getMethod(String methodName, Class<?> clasz)
{
    try
    {
        Method[] methods = clasz.getMethods();
        for (Method method : methods)
        {
            if (methodName.equals(method.getName()))
            {
                Class<?>[] params = method.getParameterTypes();
                if (params.length == 1)
                {
                    Class<?> param = params[0];
                    if ((param == int.class) || (param == float.class) || (param == float.class))
                    {
                        //method.invoke(object, value);
                        return method;
                    }
                    else if (param.isAssignableFrom(Number.class))
                    {
                        return method;
                    }
                    //else if (...)
                    //{
                    //    ...
                    //}
                }
            }
        }
    }
    catch (Exception e)
    {
        //some handling
    }
    return null;
}

この例では、メソッドは、、、、またはのスーパークラスでgetMethod(String, Class<?>)あるパラメーターを1つだけ持つメソッドを返します。intfloatdoubleNumber

これは基本的な実装です-法案に適合する最初のメソッドを返します。一致するすべてのメソッドのリストを作成するためにそれを拡張し、次にそれらをある種の基準に従ってソートし、最も一致するメソッドを返す必要があります。

次に、より一般的なgetMethod(String, Class<?>)メソッドを作成して、考えられる「厳密な一致」シナリオをさらに処理し、場合によっては複数のパラメーターを処理することで、さらに進んでいくことができます。

HTH


編集:@finnwが指摘しているように、サンプルコードにあるように、オブジェクトClass#isAssignableFrom(Class<?> cls)とは別にプリミティブをテストするという制限があるため、を使用するときは注意してください。Number

于 2010-02-09T03:43:09.480 に答える
2

他の人が指摘しているように、これを行う標準的な方法はないため、独自のオーバーロード解決アルゴリズムを実装する必要があります。

javac のオーバーロード解決規則にできるだけ厳密に従うことはおそらく理にかなっているでしょう :
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#292575型のスクリプト言語を使用できますが、コンパイラが自動的に生成するブリッジ メソッド
の恩恵を受ける可能性があります。

注意すべきいくつかの落とし穴:

  • Class.isAssignableFrom自動拡張プリミティブ変換については知りません。これらはコンパイラーに実装されたシンタックス シュガーであるためです。VM またはクラス階層では発生しません。例えばint.class.isAssignableFrom(short.class)返品false
  • 同様にClass.isAssignableFrom、自動ボクシングについても知りません。 Integer.class.isAssignableFrom(int.class)戻りますfalse
  • Class.isInstanceを引数としてClass.cast取ります。Objectそれらにプリミティブ値を渡すことはできません。も返すため、ボックス化解除Objectには使用できません ( (int) new Integer(42)Java ソースでは有効ですがint.class.cast(new Integer(42))、例外がスローされます)。
于 2010-02-09T11:34:46.950 に答える
1

AFAIK、この種のことを行う簡単な方法はありません。確かに、これを行うための標準のJavaクラスライブラリには何もありません。

問題は、単一の「正しい」答えがないことです。すべてのユースケースを検討し、「適切な方法」を決定し、それに応じてリフレクションコードを実装する必要があります。

于 2010-02-09T01:14:36.053 に答える