2008-10-08 50 views
15

测试(使用反射)最简单的方法是什么,给定的方法(即java.lang.Method实例)是否有返回类型,可以安全地转换为列表<字符串>?如何测试方法返回类型是否匹配列表<String>

考虑这个片断:

public static class StringList extends ArrayList<String> {} 

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

所有方法1,2,3符合要求。对于方法1(通过返回ParameterizedType的实例的getGenericReturnType())来测试它非常容易,但对于方法2和3来说,它并不那么明显。我想,通过遍历所有getGenericSuperclass()和getGenericInterfaces(),我们可以得到非常接近,但我不明白,如何将列表<E>中的TypeVariable(它发生在超类接口中的某处)与实际类型参数(即,这个E与String匹配的地方)。

或者也许有一种完全不同的(更容易)的方式,我忽略了吗?

编辑:对于那些寻找到它,这里是方法4,这也满足了要求,这说明一些情况,其中有可查:

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

哇,我很惊讶这是如此复杂。我刚测试并了解到(obj instanceof List )不能编译!跆拳道? – Kip 2008-10-08 17:52:36

回答

4

解决这个问题一般来说并不容易,只使用Java本身提供的工具。有很多特殊情况需要照顾(嵌套类,类型参数边界......)。 这就是为什么我写了一个库来使泛型反射更容易:gentyref。我添加了示例代码(以JUnit测试的形式),以显示如何使用它来解决此问题:StackoverflowQ182872Test.java。基本上,您只需拨打GenericTypeReflector.isSuperType即可使用TypeToken(来自Neil Gafter的想法)查看List<String>是否是返回类型的超类型。

我还添加了第五个测试用例,表明在返回类型(GenericTypeReflector.getExactReturnType)上使用它们的值替换类型参数时需要额外的转换。

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

此输出:

 
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

这在非常简单的情况下工作,但这个解决方案有很多错误;它做出了许多假设。 只是一个简单的例子,出了什么问题: 采取:接口我扩展名单 {} 它认为我 Integer也是List的类型参数,而不是String。 – 2009-01-01 21:33:19

0

This thread在java.net论坛可能会有所帮助(虽然我不得不承认我听不懂他们说的一切)。

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的链。

相关问题