16

指定されたメソッド (つまり、java.lang.Method インスタンス) に List<String> に安全にキャストできる戻り値の型があるかどうかを (リフレクションを使用して) テストする最も簡単な方法は何ですか?

次のスニペットを検討してください。

public static class StringList extends ArrayList<String> {}

public List<String> method1();
public ArrayList<String> method2();
public StringList method3();

方法 1、2、3 はすべて要件を満たしています。(ParameterizedType のインスタンスを返す getGenericReturnType() を使用して) method1 をテストするのは非常に簡単ですが、method2 と 3 についてはそれほど明白ではありません。すべての getGenericSuperclass() と getGenericInterfaces() をトラバースすることで、かなり近づけることができると思いますが、List<E> の TypeVariable (スーパークラス インターフェイスのどこかで発生) を実際のtype パラメーター (つまり、この E が String に一致する場所)。

それとも、私が見落としている完全に異なる(より簡単な)方法がありますか?

EDIT:それを調べている人のために、ここにmethod4があります。これも要件を満たし、調査する必要があるいくつかのケースを示しています:

public interface Parametrized<T extends StringList> {
    T method4();
}
4

4 に答える 4

9

このコードを試してみたところ、実際のジェネリック型クラスが返されるため、型情報を取得できるようです。ただし、これは方法 1 と 2 でのみ機能します。方法 3 は、投稿者が想定しているため、文字列型のリストを返さないようで、失敗します。

public class Main {
/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    try{
        Method m = Main.class.getDeclaredMethod("method1", new Class[]{});
        instanceOf(m, List.class, String.class);
        m = Main.class.getDeclaredMethod("method2", new Class[]{});
        instanceOf(m, List.class, String.class);
        m = Main.class.getDeclaredMethod("method3", new Class[]{});
        instanceOf(m, List.class, String.class);
        m = Main.class.getDeclaredMethod("method4", new Class[]{});
        instanceOf(m, StringList.class);
    }catch(Exception e){
        System.err.println(e.toString());
    }
}

public static boolean instanceOf (
        Method m, 
        Class<?> returnedBaseClass, 
        Class<?> ... genericParameters) {
    System.out.println("Testing method: " + m.getDeclaringClass().getName()+"."+ m.getName());
    boolean instanceOf = false;
    instanceOf = returnedBaseClass.isAssignableFrom(m.getReturnType());
    System.out.println("\tReturn type test succesfull: " + instanceOf + " (expected '"+returnedBaseClass.getName()+"' found '"+m.getReturnType().getName()+"')");
    System.out.print("\tNumber of generic parameters matches: ");
    Type t = m.getGenericReturnType();
    if(t instanceof ParameterizedType){
        ParameterizedType pt = (ParameterizedType)t;
        Type[] actualGenericParameters = pt.getActualTypeArguments();
        instanceOf = instanceOf
            && actualGenericParameters.length == genericParameters.length;
        System.out.println("" + instanceOf + " (expected "+ genericParameters.length +", found " + actualGenericParameters.length+")");
        for (int i = 0; instanceOf && i < genericParameters.length; i++) {
            if (actualGenericParameters[i] instanceof Class) {
                instanceOf = instanceOf
                        && genericParameters[i].isAssignableFrom(
                            (Class) actualGenericParameters[i]);
                System.out.println("\tGeneric parameter no. " + (i+1) + " matches: " + instanceOf + " (expected '"+genericParameters[i].getName()+"' found '"+((Class) actualGenericParameters[i]).getName()+"')");
            } else {
                instanceOf = false;
                System.out.println("\tFailure generic parameter is not a class");
            }
        }
    } else {
        System.out.println("" + true + " 0 parameters");
    }
    return instanceOf;
}
public List<String> method1() {
    return null;
}
public ArrayList<String> method2() {
    return new ArrayList<String>();
}
public StringList method3() {
    return null;
}
public <T extends StringList> T method4() {
    return null;
}

これは以下を出力します:

テスト方法: javaapplication2.Main.method1
        戻り型テストの成功: true (予期された 'java.util.List' が 'java.util.List' を検出)
        ジェネリック パラメーターの一致数: true (期待される 1、見つかった 1)
        ジェネリックパラメータNo. 1 件の一致: true (予期された 'java.lang.String' が 'java.lang.String' で見つかりました)
テスト方法: javaapplication2.Main.method2
        戻り値の型テスト成功: true (予期される 'java.util.List' が 'java.util.ArrayList' を検出)
        ジェネリック パラメーターの一致数: true (期待される 1、見つかった 1)
        ジェネリックパラメータNo. 1 件の一致: true (予期された 'java.lang.String' が 'java.lang.String' で見つかりました)
テスト方法: javaapplication2.Main.method3
        戻り値の型テスト成功: false (期待される 'java.util.List' は 'com.sun.org.apache.xerces.internal.xs.StringList' を見つけました)
        ジェネリック パラメータの一致数: true 0 パラメータ
テスト方法: javaapplication2.Main.method4
        戻り型テスト成功: true (期待される 'com.sun.org.apache.xerces.internal.xs.StringList' が 'com.sun.org.apache.xerces.internal.xs.StringList' を検出)
        ジェネリック パラメータの一致数: true 0 パラメータ
于 2008-10-08T14:58:50.647 に答える
4

これを一般的に解決することは、Java 自体が提供するツールだけを使用して自分で行うのは簡単ではありません。注意が必要な特殊なケース (ネストされたクラス、型パラメーターの境界など) が多数あります。そのため、ジェネリック型のリフレクションを簡単にするライブラリを作成しました: gentyref。この問題を解決するために使用する方法を示すサンプル コードを (JUnit テストの形式で) 追加しました: StackoverflowQ182872Test.java。基本的に、 a ( Neil GafterGenericTypeReflector.isSuperTypeのアイデア) を使用して呼び出すだけで、 が戻り値の型のスーパータイプであるかどうかを確認できます。TypeTokenList<String>

また、5 番目のテスト ケースを追加して、戻り値の型 ( GenericTypeReflector.getExactReturnType) で追加の変換を行い、型パラメーターをその値に置き換える必要がある場合があることを示しました。

于 2009-01-01T22:41:48.880 に答える
0

java.netフォーラムのこのスレッドは役立つかもしれません(私は彼らが言ったことすべてを理解していなかったことを認めなければなりませんが)。

于 2008-10-08T15:44:16.033 に答える
0

あなたはすでに正しい軌道に乗っていると思います。パラメータ化された型を取得し始めるまで、 getGenericSuperclass ()と getGenericInterface()を使い続けてください...

だから基本的に:

//For string list
ParameterizedType type = (ParameterizedType)StringList.class.getGenericSuperclass();
System.out.println( type.getActualTypeArguments()[0] );

//for a descendant of string list
Class clazz = (Class)StringListChild.class.getGenericSuperclass();
ParameterizedType type = (ParameterizedType)clazz.getGenericSuperclass();
System.out.println( type.getActualTypeArguments()[0] );

これをチェックする再帰的なものを構築したいと思うでしょう。おそらく、チェーンをたどって java.util.List を探します。

于 2008-10-09T22:53:57.880 に答える