2008-10-08 15 views
15

¿Cuál es la forma más fácil de probar (utilizando reflexión), si el método dado (ejemplo java.lang.Method) tiene un tipo de retorno, que se puede convertir a List <Cadena>?Cómo comprobar si el tipo de retorno de método coincide con List <String>

Considere la siguiente:

public static class StringList extends ArrayList<String> {} 

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

Todos los métodos 1, 2, 3 cumplir con el requisito. Es bastante fácil probarlo para el método1 (a través de getGenericReturnType(), que devuelve la instancia de ParameterizedType), pero para los métodos 2 y 3, no es tan obvio. Me imagino que al atravesar getGenericSuperclass() y getGenericInterfaces(), podemos acercarnos bastante, pero no veo, cómo hacer coincidir el TypeVariable en List <E> (que se encuentra en alguna parte de las interfaces de la superclase) con el real tipo de parámetro (es decir, donde esta E se corresponde con la cadena).

¿O tal vez hay una forma completamente diferente (más fácil) de pasar por alto?

EDIT: Para aquellos que buscan en ella, aquí es método 4, que también cumple con el requisito y que muestra algunos casos más, que tienen que ser investigados:

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

Guau, me sorprende que esto sea tan complicado. ¡Acabo de probar y aprendí que (obj instanceof List ) no se compila! wtf? – Kip

Respuesta

4

Resolver esto en general no es realmente fácil hacerlo solo con las herramientas proporcionadas por Java. Hay muchos casos especiales (clases anidadas, límites de parámetros de tipo, ...) para tener en cuenta. Es por eso que escribí una biblioteca para hacer más fácil la reflexión de tipo genérico: gentyref. Agregué un código de muestra (en forma de una prueba JUnit) para mostrar cómo usarlo para resolver este problema: StackoverflowQ182872Test.java. Básicamente, simplemente llame al GenericTypeReflector.isSuperType usando TypeToken (idea desde Neil Gafter) para ver si List<String> es un supertipo del tipo de devolución.

También agregué un quinto caso de prueba, para mostrar que a veces es necesario realizar una transformación adicional en el tipo de retorno (GenericTypeReflector.getExactReturnType) para reemplazar los parámetros de tipo con sus valores.

9

Probé este código y vuelve la clase de tipo genérica real por lo que parece que se puede recuperar la información de tipo. Sin embargo, esto solo funciona para los métodos 1 y 2. El método 3 no parece devolver una cadena de caracteres escrita como el supuesto y por lo tanto falla.

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

Este salidas:

 
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 
+0

Eso funciona en casos muy simples, pero esta solución tiene muchas fallas; hace muchas suposiciones. Solo un ejemplo simple de lo que va mal: Tome: interfaz I extends List {} Cree para I que el entero es también el parámetro de tipo para List, en lugar de String. –

+0

Ha confundido la StringList definida en la descripción con una StringList de xerces. –

0

This thread en los foros java.net podría ser útil (aunque tengo que admitir que no entiendo todo lo que dijeron).

0

Creo que ya está en el camino correcto. Sólo seguir usando getGenericSuperclass() y getGenericInterface() hasta que comienza a recibir tipos parametrizados volver ...

Así que, básicamente:

//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]); 

Lo que quiere construir algo que era recursivo que comprobar esto - tal vez incluso sube la cadena buscando java.util.List.

Cuestiones relacionadas