Как проверить, соответствует ли тип возвращаемого метода методу List <String> - PullRequest
15 голосов
/ 08 октября 2008

Какой самый простой способ проверить (используя отражение), имеет ли данный метод (например, экземпляр java.lang.Method) тип возврата, который можно безопасно привести к списку ?

Рассмотрим этот фрагмент:

public static class StringList extends ArrayList<String> {}

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

Все методы 1, 2, 3 соответствуют требованию. Это довольно легко проверить для метода method1 (через getGenericReturnType (), который возвращает экземпляр ParameterizedType), но для методов 2 и 3 это не так очевидно. Я полагаю, что, обойдя все getGenericSuperclass () и getGenericInterfaces (), мы можем довольно близко, но я не вижу, как сопоставить TypeVariable в List (который происходит где-то в интерфейсах суперкласса) с фактическим параметр типа (т. е. где это E соответствует String).

Или, может быть, есть совершенно другой (более простой) способ, который я упускаю из виду?

РЕДАКТИРОВАТЬ: Для тех, кто изучает это, здесь метод4, который также выполняет требование и который показывает еще несколько случаев, которые должны быть исследованы:

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

Ответы [ 4 ]

9 голосов
/ 08 октября 2008

Я попробовал этот код, и он возвращает реальный класс универсального типа, поэтому кажется, что информация о типе может быть получена. Однако это работает только для методов 1 и 2. Похоже, что метод 3 не возвращает список с типом String, как это подразумевает плакат, и, следовательно, не может.

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;
}

Это выводит:

Testing method: javaapplication2.Main.method1
        Return type test succesfull: true (expected 'java.util.List' found 'java.util.List')
        Number of generic parameters matches: true (expected 1, found 1)
        Generic parameter no. 1 matches: true (expected 'java.lang.String' found 'java.lang.String')
Testing method: javaapplication2.Main.method2
        Return type test succesfull: true (expected 'java.util.List' found 'java.util.ArrayList')
        Number of generic parameters matches: true (expected 1, found 1)
        Generic parameter no. 1 matches: true (expected 'java.lang.String' found 'java.lang.String')
Testing method: javaapplication2.Main.method3
        Return type test succesfull: false (expected 'java.util.List' found 'com.sun.org.apache.xerces.internal.xs.StringList')
        Number of generic parameters matches: true 0 parameters
Testing method: javaapplication2.Main.method4
        Return type test succesfull: true (expected 'com.sun.org.apache.xerces.internal.xs.StringList' found 'com.sun.org.apache.xerces.internal.xs.StringList')
        Number of generic parameters matches: true 0 parameters
4 голосов
/ 02 января 2009

В общем, решить это на самом деле нелегко, используя только инструменты, предоставляемые самой Java. Есть много особых случаев (вложенные классы, границы параметров типа, ...), о которых нужно позаботиться. Вот почему я написал библиотеку, чтобы упростить отражение универсального типа: gentyref . Я добавил пример кода (в форме теста JUnit), чтобы показать, как использовать его для решения этой проблемы: StackoverflowQ182872Test.java . По сути, вы просто вызываете GenericTypeReflector.isSuperType, используя TypeToken (идея из Нейл Гафтер ), чтобы увидеть, является ли List<String> супертипом возвращаемого типа.

Я также добавил пятый тестовый пример, чтобы показать, что иногда требуется дополнительное преобразование возвращаемого типа (GenericTypeReflector.getExactReturnType) для замены параметров типа их значениями.

0 голосов
/ 10 октября 2008

Я думаю, вы уже на правильном пути. Просто продолжайте использовать 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.

0 голосов
/ 08 октября 2008

Эта ветка на форумах java.net может быть полезна (хотя я должен признать, что не понял всего, что они сказали).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...