2008-10-08 38 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的鏈。

相關問題