2015-02-05 158 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>)。我會把它作爲一個練習,讓任何想要跟隨我的腳步的人。上面的代碼應該會幫助你。