2008-10-08 6 views
15

Jaki jest najłatwiejszy sposób testowania (za pomocą refleksji), czy dana metoda (np. Instancja java.lang.Method) ma typ zwrotu, który można bezpiecznie odlać na listę < Ciąg >?Jak sprawdzić, czy typ zwracanej metody pasuje do listy <String>

Rozważmy następujący fragment:

public static class StringList extends ArrayList<String> {} 

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

Wszystkie metody 1, 2, 3 spełnić ten wymóg. Jest dość łatwy do przetestowania go dla metody 1 (przez getGenericReturnType(), która zwraca instancję parametru ParameterizedType), ale dla metod2 i 3 nie jest to tak oczywiste. Wyobrażam sobie, że przechodząc przez wszystkie getGenericSuperclass() i getGenericInterfaces(), możemy uzyskać całkiem blisko, ale nie widzę, jak dopasować TypeVariable na liście <E> (która występuje gdzieś w interfejsach nadklasy) z faktycznym parametr typu (tj. gdzie ten E jest dopasowany do łańcucha znaków).

A może jest zupełnie inny (łatwiejszy) sposób, który przeoczyłem?

EDIT: Dla tych, którzy chcą do niego, oto method4, który spełnia także wymagania i co pokazuje jeszcze kilka spraw, które mają zostać zbadane:

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

Wow, jestem zaskoczony, że to bardzo skomplikowane. Właśnie przetestowałem i dowiedziałem się, że (obj wystąpienie List ) nie kompiluje! wtf? – Kip

Odpowiedz

4

Rozwiązanie tego problemu nie jest łatwe z samego tylko użycia narzędzi dostarczonych przez samą Javę. Istnieje wiele specjalnych przypadków (zagnieżdżone klasy, ograniczenia parametrów typu, ...), którymi należy się zająć. Dlatego napisałem bibliotekę, która ułatwia generyczne odbicie tekstu: gentyref. Dodałem przykładowy kod (w formie testu JUnit), aby pokazać, jak go użyć do rozwiązania tego problemu: StackoverflowQ182872Test.java. Zasadniczo wystarczy zadzwonić pod numerprzy użyciu TypeToken (pomysł z Neil Gafter), aby sprawdzić, czy List<String> jest nadtypem typu zwracanego.

Dodałem także 5. przypadek testowy, aby pokazać, że czasami konieczna jest dodatkowa transformacja typu powrotu (GenericTypeReflector.getExactReturnType) w celu zastąpienia parametrów typu ich wartościami.

9

Próbowałem ten kod i zwraca rzeczywistej rodzajowej klasy, więc wydaje się, że można uzyskać informacje o typie. Działa to jednak tylko w przypadku metod 1 i 2. Metoda 3 nie wydaje się zwracać listy typu String, jak zakłada plakat, a zatem nie.

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

This wyjścia:

 
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

To działa w bardzo prostych przypadkach, ale jest wiele nie w tym rozwiązaniu; ma wiele założeń. Tylko jeden prosty przykład:: interfejs I rozszerza listę {} Uważa, że ​​I oznacza, że ​​liczba całkowita jest również parametrem typu dla listy, a nie dla ciągu. –

+0

Myliłeś StringList zdefiniowany w opisie z StringList z xerces. –

0

This thread na forach java.net mogą być pomocne (choć muszę przyznać, że nie rozumiem wszystkiego, co powiedział).

0

Myślę, że jesteś już na dobrej drodze. Po prostu przy użyciu getGenericSuperclass() i getGenericInterface() aż zaczną się parametryzowane typy back ...

Więc zasadniczo:

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

pewnością chcesz zbudować coś, co było rekurencyjnej, że to sprawdzić - może nawet idź w górę szukając java.util.List.

Powiązane problemy