最近正好使用到了Guava的TypeToken來獲取泛型的類型信息
比如,泛型父類需要獲取其子類定義的泛型類型時:
public abstract class GenericClazz<V> { private Class<V> classType; public void doSth() { final TypeToken<V> typeToken = new TypeToken<V>(getClass()) {}; classType = (Class<V>) typeToken.getRawType(); //獲得子類的泛型類型 } }
而使用反射,就稍微復雜了一點。
public abstract class ReflectClazz<V> { private Class<V> classType; public void doSth() { final Type genType = getClass().getGenericSuperclass(); classType = (Class<V>) ((ParameterizedType) genType).getActualTypeArguments()[0]; //獲得子類的泛型類型 } }
而當繼承類申明的泛型V也是個泛型類,如 public class SubClazz extends ReflectClazz<Map<Integer, String>> {} 這種,使用反射就會更加繁瑣……
還有一種情況,當我們需要在方法/局部變量中獲取泛型類型時,也可以使用TypeToken:
public void getGenericType() { final TypeToken typeToken = new TypeToken<List<Integer>>() {}; final Type type = typeToken.getType(); //java.util.List<java.lang.Integer> }
上面使用TypeToken的目的是為了在運行時獲取List<T>的泛型類型Integer,反射則辦不到。
(類型擦除機制,.class的LocalVariableTable屬性中只會保留Ljava/util/ArrayList,不會是 Ljava/util/ArrayList<Ljava/lang/Integer;> )
TypeToken則不是直接利用反射,而是曲線救國:創建一個TypeToken<T>的匿名繼承類。由於匿名類的申明信息中保留了泛型信息,通過反射可得…
(編譯器不會擦除聲明信息,Signatur屬性中會保存該匿名類的完整信息,如 Lcom/google/common/reflect/TypeToken<Ljava/util/List<Ljava/lang/Integer;>;>; )。