Java 如何獲取泛型類型

前言

在 Java 開發(fā)中,獲取泛型這種操作雖不是很常用,但有時確實必須的,比如 將Json 字符串反序列化成對象的時候。今天就來介紹這個操作。

場景

假設(shè)我們定義了一個類,內(nèi)部有一個數(shù)據(jù)結(jié)構(gòu),泛型為 T,當我們輸入一個 Json 字符串,想把這個 Json 反序列化成對象,那么此時,我們就需要知道這個泛型的類型。

具體代碼場景如下:

抽象父類(包含泛型):

abstract class Base<T extends Comparable<T>> {

  T data;

  public Base(String json) {
    this.data = JsonUtil.toObject(json, deSerializable());
  }
}

我們想在該類中輸入 Json,并將字符串反序列化成對象。比如下面這樣:

/**
 * 子類定義了父類
 */
class Son extends Base<DataClass> {

  public Son(String json) {
    super(json);
  }
}

/**
 * 數(shù)據(jù)類型繼承Comparable
 */
class DataClass implements Comparable<DataClass> {

  @Override
  public int compareTo(DataClass o) {
    return 0;
  }
}

上面的例子中,子類定義了泛型,但獲取泛型類型是在父類。

所以,重點在 deSerializable() 方法的實現(xiàn),我們需要一個 Class<T> 讓 Json 工具能夠正常序列化。

如何實現(xiàn)?

先說結(jié)論:通過 Java 反射包的 ParameterizedType 工具獲得泛型具體類型。

例如:下面的代碼:

  public static void main(String[] args) {
    String json = JsonUtil.toJson(new DataClass());
    Son s = new Son(json);
    Type t = s.getClass().getGenericSuperclass();
    if (t instanceof ParameterizedType) {
      System.out.println(t);
      // output: cn.think.in.java.clazz.loader.generics.Base<cn.think.in.java.clazz.loader.generics.DataClass>
      for (Type type : ((ParameterizedType) t).getActualTypeArguments()) {
        System.out.println(type);
        //output: class cn.think.in.java.clazz.loader.generics.DataClass
      }
    }
  }

首先我們將一個對象序列化成 Json 字符串,模擬外部輸入。然后呢?創(chuàng)建一個子類對象,得到這個 Son 的 Class 。
關(guān)鍵地方來了,調(diào)用 getGenericSuperclass 方法,這個方法的作用是:返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的直接超類的 Type。

所以這里會得到一個 ParameterizedTypeImpl 類型的對象。注意:這個類是 Sun 包下的,不是開源的。該類有以下幾個屬性:

Type[] 數(shù)組就是該類(我們這里是父類)的泛型,rawType 是原始類型,即 Base 的 Class 類型。而 OwnerType 返回的則是 Base 類型。

然后呢,判斷這個 t 是不是 ParameterizedType 接口的實現(xiàn)類。如果是,調(diào)用 getActualTypeArguments 方法,返回一個 Type數(shù)組,即上圖的 actualTypeArguments 屬性。

而返回的 Type 數(shù)組就是父類的泛型 Class。因為 Class 實現(xiàn)了 Type 接口。為什么是數(shù)組呢?因為每個類可以有多個泛型。

image.png

通過這樣幾行代碼,我們就得到了泛型。當然,這種用法很少。

現(xiàn)在我們知道了如何得到泛型,那么,就將剛剛的場景中的問題解決。

實現(xiàn)反序列化方法:

  private Class<T> deSerializable() {
    Type type = getClass().getGenericSuperclass();
    if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;
      System.out.println(parameterizedType.getActualTypeArguments()[0]);
      return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }
    throw new RuntimeException();
  }

獲取到當前類(Son)的泛型 Class,獲取到泛型數(shù)組,返回第一個(因為我們只有一個泛型)泛型類型的 Class<T>。
然后,使用 Json 工具傳入 Json 字符串和 Class<T> 類型并返回實體對象。

這樣就能夠保證編譯不會錯誤,且高度靈活。

這里有一個地方需要注意:Java 的泛型是會在運行期擦除的,但并不總是擦除成 Object ,而是擦除到上限類型。
如果時獲取接口的泛型則是調(diào)用 Class 的 getGenericInterfaces 方法得到接口集合的泛型。

總結(jié)

因為歷史原因,Java 的泛型一直是個痛點,但無法避免,所以使用起來確實有點麻煩。但通過 Class 類的眾多反射功能,我們還是能夠處理泛型的問題。

我們今天使用反射得到了一個類的泛型,并在父類進行處理,成功的將一個字符串反序列化成一個對象。

bye!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • 一、Java 簡介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O(shè)計...
    子非魚_t_閱讀 4,538評論 1 44
  • 轉(zhuǎn)載自http://blog.csdn.net/fenfenmiao/article/details/52165472
    xbinng閱讀 316評論 0 1
  • 想把你寫進詩里 明明我一次也未曾見過你 但我知道這不重要 總有一種情感 簡單卻濃烈 跨越了語言和碰觸 這怕是一種信...
    愛吃肥肉的胖子閱讀 190評論 0 2
  • 今想念 兒時黃瓜秧水 也念茅廬艾香 許諾忌談凄涼 總憶起你溫暖的胸膛 在他鄉(xiāng) 粽葉疊成小船 載滿一腔思念 你雙肩化...
    趙奇越閱讀 356評論 0 0

友情鏈接更多精彩內(nèi)容