1. 程式人生 > >Java Reflection(九):泛型

Java Reflection(九):泛型

原文地址 作者: Jakob Jenkov 譯者:葉文海([email protected]

我常常在一些文章以及論壇中讀到說Java泛型資訊在編譯期被擦除(erased)所以你無法在執行期獲得有關泛型的資訊。其實這種說法並不完全正確的,在一些情況下是可以在執行期獲取到泛型的資訊。這些情況其實覆蓋了一些我們需要泛型資訊的需求。在本節中我們會演示一下這些情況。

運用泛型反射的經驗法則

下面是兩個典型的使用泛型的場景:
1、宣告一個需要被引數化(parameterizable)的類/介面。
2、使用一個引數化類。

當你宣告一個類或者介面的時候你可以指明這個類或介面可以被引數化,java.util.List介面就是典型的例子。你可以運用泛型機制建立一個標明儲存的是String型別list,這樣比你建立一個Object的list要更好。

當你想在執行期引數化型別本身,比如你想檢查java.util.List類的引數化型別,你是沒有辦法能知道他具體的引數化型別是什麼。這樣一來這個型別就可以是一個應用中所有的型別。但是,當你檢查一個使用了被引數化的型別的變數或者方法,你可以獲得這個被引數化型別的具體引數。總之:

你不能在執行期獲知一個被引數化的型別的具體引數型別是什麼,但是你可以在用到這個被引數化型別的方法以及變數中找到他們,換句話說就是獲知他們具體的引數化型別。
在下面的段落中會向你演示這類情況。

泛型方法返回型別

如果你獲得了java.lang.reflect.Method物件,那麼你就可以獲取到這個方法的泛型返回型別資訊。如果方法是在一個被引數化型別之中(譯者注:如T fun())那麼你無法獲取他的具體型別,但是如果方法返回一個泛型類(譯者注:如List fun())那麼你就可以獲得這個泛型類的具體引數化型別。你可以在“

Java Reflection: Methods”中閱讀到有關如何獲取Method物件的相關內容。下面這個例子定義了一個類這個類中的方法返回型別是一個泛型型別:

  public class MyClass {

  protected List<String> stringList = ...;

  public List<String> getStringList(){
    return this.stringList;
  }
}

我們可以獲取getStringList()方法的泛型返回型別,換句話說,我們可以檢測到getStringList()方法返回的是List而不僅僅只是一個List。如下例:

Method method = MyClass.class.getMethod("getStringList", null);

Type returnType = method.getGenericReturnType();

if(returnType instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) returnType;
    Type[] typeArguments = type.getActualTypeArguments();
    for(Type typeArgument : typeArguments){
        Class typeArgClass = (Class) typeArgument;
        System.out.println("typeArgClass = " + typeArgClass);
    }
}

這段程式碼會打印出 “typeArgClass = java.lang.String”,Type[]陣列typeArguments只有一個結果 – 一個代表java.lang.String的Class類的例項。Class類實現了Type介面。

泛型方法引數型別

你同樣可以通過反射來獲取方法引數的泛型型別,下面這個例子定義了一個類,這個類中的方法的引數是一個被引數化的List:

public class MyClass {
  protected List<String> stringList = ...;

  public void setStringList(List<String> list){
    this.stringList = list;
  }
}

你可以像這樣來獲取方法的泛型引數:

method = Myclass.class.getMethod("setStringList", List.class);

Type[] genericParameterTypes = method.getGenericParameterTypes();

for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        ParameterizedType aType = (ParameterizedType) genericParameterType;
        Type[] parameterArgTypes = aType.getActualTypeArguments();
        for(Type parameterArgType : parameterArgTypes){
            Class parameterArgClass = (Class) parameterArgType;
            System.out.println("parameterArgClass = " + parameterArgClass);
        }
    }
}

這段程式碼會打印出”parameterArgType = java.lang.String”。Type[]陣列parameterArgTypes只有一個結果 – 一個代表java.lang.String的Class類的例項。Class類實現了Type介面。

泛型變數型別

同樣可以通過反射來訪問公有(Public)變數的泛型型別,無論這個變數是一個類的靜態成員變數或是例項成員變數。你可以在“Java Reflection: Fields”中閱讀到有關如何獲取Field物件的相關內容。這是之前的一個例子,一個定義了一個名為stringList的成員變數的類。

public class MyClass {
  public List<String> stringList = ...;
}
Field field = MyClass.class.getField("stringList");

Type genericFieldType = field.getGenericType();

if(genericFieldType instanceof ParameterizedType){
    ParameterizedType aType = (ParameterizedType) genericFieldType;
    Type[] fieldArgTypes = aType.getActualTypeArguments();
    for(Type fieldArgType : fieldArgTypes){
        Class fieldArgClass = (Class) fieldArgType;
        System.out.println("fieldArgClass = " + fieldArgClass);
    }
}

這段程式碼會打印出”fieldArgClass = java.lang.String”。Type[]陣列fieldArgClass只有一個結果 – 一個代表java.lang.String的Class類的例項。Class類實現了Type介面。