2015-02-05 114 views
4

考虑这个做作类:如何从参数化类型的方法参数中获取参数化类型的类?

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

当使用反射来检查这个方法,我怎么能得到较java.lang.String(甚至串"java.lang.String")的Class对象的论据chooseRandom看什么时候?

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

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

参数化类型java.util.Listjava.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其他一些问题和答案,其中没有一个似乎真的来回答这个问题,或者,如果他们这样做,有没有与之相关的工作代码和解释是...薄。

回答

0

Method.getGenericParameterTypes()将返回一个ParameterizedType为通用参数,其可以被检查,以提取它的类型的参数。所以Test.class.getMethod("chooseRandom", List.class).getGenericParameterTypes()应该返回一个包含ParameterizedType的单元素数组。

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 

在实践中,如果你不知道,有至少一个参数,它是通用的,有一种说法,当然,你会添加一些检查。

+0

这让我的大脑在我需要它的地方。起初,我接受了这个答案,但我会发布我自己的答案,因为它确实更加充实。 – 2015-02-06 01:15:32

0

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(")"); 
} 

我离开时的签名是不是令人兴奋的部分只会让事情变得混乱:可见性和其他标志,异常类型等。

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

protected void parseLocalesHeader(String string, 
            java.util.TreeMap<Double,java.util.ArrayList<java.util.Locale>> treeMap) 

注意,这目前不处理的Type几个实现,比如作为TypeVariable(这看起来像<T extends Serializable>)。我会把它作为一个练习,让任何想要跟随我的脚步的人。上面的代码应该会帮助你。