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

How can I get the parameterized-type class from a parameterized-type method argument?

考虑这个人为的 class:

import java.util.List;
public class Test {
    public String chooseRandom(List<String> strings) {
        return null;
    }
}

使用反射检查此方法时,如何在查看 [=17] 的参数时获取表示 java.lang.String(甚至字符串 "java.lang.String")的 Class 对象=]?

我知道 Java 在编译时擦除类型,但它们必须仍然存在,因为 javap 可以正确打印它们。 运行 javap 在此编译后 class 结果:

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

这给了我 java.util.List<E>typeName。我希望的是 java.util.List<java.lang.String>.

我已经阅读了一些关于 SO 的其他问题和答案,其中 none 似乎真的回答了这个问题,或者即使他们回答了,也没有与之相关的工作代码,解释是 . ..瘦.

Method.getGenericParameterTypes() will return a ParameterizedType 用于泛型参数,可以对其进行检查以提取其类型参数。所以 Test.class.getMethod("chooseRandom", List.class).getGenericParameterTypes() 应该 return 一个包含 ParameterizedType.

的单元素数组

使用getGenericParameterTypes and 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

在实践中,如果您不知道至少有一个参数并且它是通用的并且具有一种类型参数,那么您当然必须添加一些检查。

Java 的类型擦除意味着参数 (java.util.List) 的 "true type" 不包含通用类型信息。您不能简单地使用 Method.getParameterTypes()[i] 并期望从那里提取通用参数类型(在本例中为 String)。相反,您必须使用 Method.getGenericParameterTypes,其中 returns 有点无用的 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>)。我将把它作为练习留给任何想跟随我的脚步的人。上面的代码应该让你上路。