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

Chr*_*ltz 6 java generics reflection parameters

考虑这个人为的类:

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

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

我知道 Java 在编译时擦除类型,但它们必须仍然存在,因为javap可以正确打印它们。javap在这个编译的类上运行会导致:

Compiled from "Test.java"
public class Test {
  public Test();
  public java.lang.String chooseRandom(java.util.List<java.lang.String>);
}
Run Code Online (Sandbox Code Playgroud)

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 + ">";
}
Run Code Online (Sandbox Code Playgroud)

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

我已经阅读了其他一些关于 SO 的问题和答案,但似乎没有一个真的能回答这个问题,或者如果回答了,也没有与它们相关的工作代码,而且解释也很简单。

Chr*_*ltz 4

Java 的类型擦除意味着参数 ( 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(")");
}
Run Code Online (Sandbox Code Playgroud)

我省略了签名中那些不那么令人兴奋并且只会让事情变得混乱的部分:可见性和其他标志、异常类型等。

上面的代码可以正确解码我试图获取更多信息的真实方法的类型签名:

protected void parseLocalesHeader(String string,
                                  java.util.TreeMap<Double,java.util.ArrayList<java.util.Locale>> treeMap)
Run Code Online (Sandbox Code Playgroud)

请注意,这目前不处理 的一些实现Type,例如TypeVariable(看起来像 的那个<T extends Serializable>)。我将把它作为一个练习留给任何想要追随我脚步的人。上面的代码应该可以帮助您上路。