java 如何测试方法返回类型是否匹配 List<String>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/182872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to test whether method return type matches List<String>
提问by Michal
What is the easiest way to test (using reflection), whether given method (i.e. java.lang.Method instance) has a return type, which can be safely casted to List<String>?
什么是最简单的测试方法(使用反射),给定的方法(即 java.lang.Method 实例)是否具有返回类型,可以安全地转换为 List<String> ?
Consider this snippet:
考虑这个片段:
public static class StringList extends ArrayList<String> {}
public List<String> method1();
public ArrayList<String> method2();
public StringList method3();
All methods 1, 2, 3 fulfill the requirement. It's quite easy to test it for the method1 (via getGenericReturnType(), which returns instance of ParameterizedType), but for methods2 and 3, it's not so obvious. I imagine, that by traversing all getGenericSuperclass() and getGenericInterfaces(), we can get quite close, but I don't see, how to match the TypeVariable in List<E> (which occurs somewhere in the superclass interfaces) with the actual type parameter (i.e. where this E is matched to String).
所有方法1、2、3都满足要求。为method1 测试它很容易(通过getGenericReturnType(),它返回ParameterizedType 的实例),但对于method2 和3,它不是那么明显。我想,通过遍历所有 getGenericSuperclass() 和 getGenericInterfaces(),我们可以非常接近,但我看不到,如何将 List<E> 中的 TypeVariable(发生在超类接口中的某处)与实际类型参数(即此 E 与字符串匹配的位置)。
Or maybe is there a completely different (easier) way, which I overlook?
或者也许有一种完全不同(更简单)的方式,我忽略了?
EDIT:For those looking into it, here is method4, which also fulfills the requirement and which shows some more cases, which have to be investigated:
编辑:对于那些正在研究它的人,这里是 method4,它也满足了要求,并显示了更多需要调查的情况:
public interface Parametrized<T extends StringList> {
T method4();
}
采纳答案by Wouter Coekaerts
Solving this in general is really not easy to do yourself using only the tools provided by Java itself. There are a lot of special cases (nested classes, type parameter bounds,...) to take care of.
That's why I wrote a library to make generic type reflection easier: gentyref. I added sample code (in the form of a JUnit test) to show how to use it to solve this problem: StackoverflowQ182872Test.java. Basically, you just call GenericTypeReflector.isSuperTypeusing a TypeToken(idea from Neil Gafter) to see if List<String>is a supertype of the return type.
一般来说,仅使用 Java 本身提供的工具自己解决这个问题真的不容易。有很多特殊情况(嵌套类、类型参数边界等)需要处理。这就是为什么我编写了一个库来简化泛型类型反射:gentyref。我添加了示例代码(以 JUnit 测试的形式)来展示如何使用它来解决这个问题:StackoverflowQ182872Test.java。基本上,您只需调用GenericTypeReflector.isSuperType使用TypeToken(来自Neil Gafter 的想法)来查看是否List<String>是返回类型的超类型。
I also added a 5th test case, to show that an extra transformation on the return type (GenericTypeReflector.getExactReturnType) to replace type parameters with their values is sometimes needed.
我还添加了第 5 个测试用例,以表明GenericTypeReflector.getExactReturnType有时需要对返回类型 ( ) 进行额外转换以将类型参数替换为其值。
回答by Jasper
I tried this code and it returns the actual generic type class so it seems the type info can be retrieved. However this only works for method 1 and 2. Method 3 does not seem to return a list typed String as the poster assumes and therefore fails.
我尝试了这段代码,它返回实际的泛型类型类,因此似乎可以检索类型信息。然而,这仅适用于方法 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;
}
This outputs:
这输出:
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
回答by Michael Myers
This threadon the java.net forums might be helpful (although I have to admit I didn't understand everything they said).
java.net 论坛上的这个主题可能会有所帮助(尽管我不得不承认我并不理解他们所说的一切)。
回答by James Schek
I think you are already on the right track. Just keep using getGenericSuperclass()and getGenericInterface() until you start getting parameterized types back...
我认为你已经在正确的轨道上。继续使用getGenericSuperclass()和 getGenericInterface() 直到你开始获取参数化类型......
So basically:
所以基本上:
//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] );
You'd want to build something that was recursive that would check for this--maybe even go up the chain looking for java.util.List.
您可能想要构建一些递归的东西来检查这个——甚至可能去寻找 java.util.List 的链条。

