如何从参数化类型方法参数中获取参数化类型类?

时间:2015-02-05 21:36:10

标签: java generics reflection parameters

考虑这个人为的课程:

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上阅读了其他一些问题和答案,但他们似乎都没有真正回答这个问题,或者如果他们这样做,就没有与之相关的工作代码,并且解释很简单。

3 个答案:

答案 0 :(得分:3)

使用getGenericParameterTypesgetActualTypeArguments方法:

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>的那个)。对于那些想跟随我的脚步的人来说,我会把它留作练习。上面的代码应该可以让你上路。