考虑这个人为的课程:
import java.util.List;
public class Test {
public String chooseRandom(List<String> strings) {
return null;
}
}
使用反射来检查此方法时,在查看Class
的参数时,如何获取表示java.lang.String
的{{1}}对象(甚至是字符串"java.lang.String"
) ?
我知道Java会在编译时擦除类型,但它们仍然必须存在,因为chooseRandom
可以正确打印它们。在此编译的类上运行javap
会导致:
javap
Compiled from "Test.java"
public class Test {
public Test();
public java.lang.String chooseRandom(java.util.List<java.lang.String>);
}
(java.util.List
)的参数化类型绝对可用......我无法找出它是什么。
我试过这个:
java.lang.String
这为我提供了Class clazz = [grab the type of chooseRandom's parameter list's first argument];
String typeName = clazz.getName();
TypeVariable[] genericTypes = clazz.getTypeParameters();
if(null != genericTypes && 0 < genericTypes.length)
{
boolean first = true;
typeName += "<";
for(TypeVariable type : genericTypes)
{
if(first) first = false;
else typeName += ",";
typeName += type.getTypeName();
}
typeName = typeName + ">";
}
typeName
。我希望的是java.util.List<E>
。
我已经在SO上阅读了其他一些问题和答案,但他们似乎都没有真正回答这个问题,或者如果他们这样做,就没有与之相关的工作代码,并且解释很简单。
答案 0 :(得分:3)
使用getGenericParameterTypes
和getActualTypeArguments
方法:
Method m = Test.class.getMethod("chooseRandom", List.class);
Type t = m.getGenericParameterTypes()[0];
// we know, in this case, that t is a ParameterizedType
ParameterizedType p = (ParameterizedType) t;
Type s = p.getActualTypeArguments()[0]; // class java.lang.String
在实践中,如果你不知道至少有一个参数,并且它是通用的并且有一个类型参数,当然,你必须添加一些检查。
答案 1 :(得分:0)
Method.getGenericParameterTypes()
将为泛型参数返回ParameterizedType
,可以检查这些参数以提取其类型参数。因此Test.class.getMethod("chooseRandom", List.class).getGenericParameterTypes()
应返回包含ParameterizedType
的单元素数组。
答案 2 :(得分:0)
Java类型的擦除意味着&#34;真正的类型&#34;参数(java.util.List
)的值不包含泛型类型信息。您不能简单地使用Method.getParameterTypes()[i]
并期望从那里提取通用参数类型(在这种情况下为String
)。相反,你必须使用Method.getGenericParameterTypes
来返回有点无用的java.lang.reflect.Type
接口的实现的整个动画片。
最后,这里的代码提供了一个如何做我想要做的所有的例子:
public String getDataType(Class clazz)
{
if(clazz.isPrimitive())
return clazz.getName();
if(clazz.isArray())
return getDataType(clazz.getComponentType()) + "[]";
String typeName;
if("java.lang".equals(clazz.getPackage().getName()))
typeName = clazz.getName().substring(10);
else
typeName = clazz.getName();
return typeName;
}
public String getDataType(Type type)
{
if(type instanceof Class)
return getDataType((Class)type);
if(type instanceof ParameterizedType)
{
ParameterizedType pt = (ParameterizedType)type;
StringBuilder typeName = new StringBuilder(getDataType(pt.getRawType()));
Type[] specificTypes = pt.getActualTypeArguments();
if(null != specificTypes && 0 < specificTypes.length)
{
typeName.append("<");
for(int j=0; j<specificTypes.length; ++j)
{
if(j > 0)
typeName.append(",");
typeName.append(getDataType(specificTypes[j]));
}
typeName.append(">");
}
return typeName.toString();
}
return "[" + type + ", a " + type.getClass().getName() + "]";
}
public void getMethodSignature(Method m)
{
System.out.print(getDataType(m.getGenericReturnType());
System.out.print(" ");
System.out.print(method.getName());
System.out.print("(");
Type[] parameterTypes = method.getGenericParameterTypes();
if(null != parameterTypes && 0 < parameterTypes.length)
{
for(int i=0; i<parameterTypes.length; ++i)
{
if(0<i) System.out.print(",");
System.out.print(getDataType(parameterTypes[i]));
}
}
System.out.println(")");
}
我遗漏了签名的部分,这些部分并不令人兴奋,只会让事情变得混乱:可见性和其他标志,异常类型等。
上面的代码可以正确解码我试图查询的 real 方法的类型签名以获取更多信息:
protected void parseLocalesHeader(String string,
java.util.TreeMap<Double,java.util.ArrayList<java.util.Locale>> treeMap)
请注意,目前这不会处理Type
的一些实现,例如TypeVariable
(看起来像<T extends Serializable>
的那个)。对于那些想跟随我的脚步的人来说,我会把它留作练习。上面的代码应该可以让你上路。