180530-反射獲取泛型類的實際參數

文章連接:https://liuyueyi.github.io/hexblog/2018/05/30/180530-經過反射獲取泛型類的實際參數/css

反射獲取泛型類的實際參數

泛型用得仍是比較多的,那麼如何獲取泛型類上實際的參數類型呢?java

好比一個接口爲git

public interface IBolt<T, K> { } 

如今給一個IBolt的具體實現類,能夠獲取到實際的參數類型麼?下面幾種case能夠怎麼獲取實際的IBolt中的T和K類型呢?github

// 實現接口方式 public class ABolt implements IBolt<String, Boolean>{} public class AFBolt<T> implements IBolt<String, T> {} public interface EBolt<T> extends IBolt<String, T> {} public class AEBolt implements EBolt<Boolean> {} public interface RBolt extends IBolt<String, Boolean>{} public class ARBolt implements RBolt{} // 繼承抽象類方式 public abstract class AbsBolt<T,K> implements IBolt<T,K> {} public class BBolt extends AbsBolt<String, Boolean> {} public abstract class EAbsBolt<T> implements IBolt<String, T> {} public class BEBolt extends EAbsBolt<Boolean> {} 

I. 基本姿式

首先拿最簡單的兩個case來進行分析,一個是 ABolt, 一個是BBolt,根據這兩個類信息來獲取對應的泛型類型;工具

1. 接口實現方式獲取

主要藉助的就是右邊這個方法:java.lang.Class#getGenericInterfacesoop

a. 簡單對比

  1. Type[] getGenericInterfaces

以Type的形式返回本類直接實現的接口.這樣就包含了泛型參數信息學習

  1. Class[] getInterfaces

返回本類直接實現的接口.不包含泛型參數信息編碼

b. 編碼實現

一個基礎的實現方式以下url

@Test public void testGetTypes() { Type[] types = ABolt.class.getGenericInterfaces(); ParameterizedType ptype; for (Type type: types) { if (!(type instanceof ParameterizedType)) { // 非泛型類型,直接丟掉 continue; } ptype = (ParameterizedType) type; if (IBolt.class.equals(ptype.getRawType())) { // 若是正好是咱們須要獲取的IBolt對象,則直接獲取 Type[] parTypes = ptype.getActualTypeArguments(); for (Type par: parTypes) { System.out.println(par.getTypeName()); } } } } 

簡單分析上面實現:spa

  • 首先是獲取全部的接口信息,遍歷接口,
  • 若是這個接口是支持泛型的,則返回的type應該是ParameterizedType類型
  • 獲取原始類信息(主要目的是爲了和目標類進行對比 IBolt.class.equals(ptype.getRawType())
  • 獲取泛型類型 ptype.getActualTypeArguments()

輸出結果以下:

java.lang.String java.lang.Boolean 

上面這個實現針對ABolt還能夠,可是換成 AEBolt 以後,即非直接實現目標接口的狀況下,發現什麼都獲取不到,由於 IBolt.class.equals(ptype.getRawType()) 這個條件不會知足,稍稍改一下,改爲只要是IBolt的子類便可

@Test public void testGetTypes() { Type[] types = AEBolt.class.getGenericInterfaces(); ParameterizedType ptype; for (Type type: types) { if (!(type instanceof ParameterizedType)) { // 非泛型類型,直接丟掉 continue; } ptype = (ParameterizedType) type; if (ptype.getRawType() instanceof Class && IBolt.class.isAssignableFrom((Class<?>) ptype.getRawType())) { // 若是正好是咱們須要獲取的IBolt對象,則直接獲取 Type[] parTypes = ptype.getActualTypeArguments(); for (Type par: parTypes) { System.out.println(par.getTypeName()); } } } } 

此時輸出爲以下,實際上只是EBolt上的泛型類型,與咱們指望的輸出 (String, Boolean) 不符,後面再說

java.lang.Boolean 

2. 抽象類繼承方式獲取

抽象類與接口的主要區別在於類是單繼承的,因此改爲用 java.lang.Class#getGenericSuperclass 獲取

a. 簡單對比

  1. Type getGenericSuperclass()

返回父類的基本類信息,包含泛型參數信息

  1. Class<? super T> getSuperclass();

返回父類信息,不包含泛型

b. 代碼實現

同上面的差很少,針對BBolt的實現,能夠這麼來

@Test public void testGetAbsTypes() { Class basicClz = BBolt.class; Type type; ParameterizedType ptype; while (true) { if (Object.class.equals(basicClz)) { break; } type = basicClz.getGenericSuperclass(); if (!(type instanceof ParameterizedType)) { basicClz = basicClz.getSuperclass(); continue; } ptype = (ParameterizedType) type; if (ptype.getRawType() instanceof Class && IBolt.class.isAssignableFrom((Class<?>) ptype.getRawType())) { Type[] parTypes = ptype.getActualTypeArguments(); for (Type par : parTypes) { System.out.println(par.getTypeName()); } break; } else { basicClz = basicClz.getSuperclass(); } } } 

針對上面代碼簡單進行分析,步驟以下:

  • 獲取父類(包含泛型)信息
  • 若是父類沒有泛型信息,則繼續往上獲取父類信息
  • 包含泛型信息以後,判斷這個類是否爲咱們預期的目標類 IBolt.class.isAssignableFrom((Class<?>) ptype.getRawType())
  • 若是是,則直接獲取參數信息

輸出結果以下:

java.lang.String java.lang.Boolean 

固然上面依然是存在和上面同樣的問題,對於BEBolt這個類,輸出的就和咱們預期的不一樣,其輸出只會有 EAbsBolt<Boolean> 上的信息,即到獲取EAbsBolt這一層時,就結束了

java.lang.Boolean 

若是咱們將上面的斷定當前類是否爲Ibolt.class,會輸出什麼呢?

  • 什麼都沒有,由於Ibolt是接口,而獲取父類是獲取不到接口信息的,因此斷定永遠走不進去

II. 進階實現

上面的基礎實現中,都存在一些問題,特別是但繼承結構比較複雜,深度較大時,其中又穿插着泛型類,致使不太好獲取精確的類型信息,下面進行嘗試探索,不保證能夠成功

1. 接口實現方式

主要的目標就是能正常的分析AEBolt這個case,嘗試思路以下:

  • 層層往上,直到目標接口,而後獲取參數類型

改進後的實現以下

@Test public void testGetTypes() { // Class basicClz = ARBolt.class; Class basicClz = AEBolt.class; Type[] types; ParameterizedType ptype; types = basicClz.getGenericInterfaces(); boolean loop = false; while (true) { if (types.length == 0) { break; } for (Type type : types) { if (type instanceof Class) { if (IBolt.class.isAssignableFrom((Class<?>) type)) { // 即表示有一個繼承了IBolt的接口,完成了IBolt的泛型參數定義 // 如: public interface ARBolt extends IBolt<String, Boolean> types = ((Class) type).getGenericInterfaces(); loop = true; break; } else { // 不是預期的類,直接pass掉 continue; } } ptype = (ParameterizedType) type; if (ptype.getRawType() instanceof Class) { if (!IBolt.class.isAssignableFrom((Class<?>) ptype.getRawType())) { continue; } if (IBolt.class.equals(ptype.getRawType())) { // 若是正好是咱們須要獲取的IBolt對象,則直接獲取 Type[] parTypes = ptype.getActualTypeArguments(); for (Type par : parTypes) { System.out.println(par.getTypeName()); } return; } else { // 須要根據父類來獲取參數信息,從新進入循環 types = ((Class) ptype.getRawType()).getGenericInterfaces(); loop = true; break; } } } if (!loop) { break; } } } 

上面的實現相比較以前的負責很多,首先來看針對 AEBolt 而言,輸出爲

java.lang.String T 

若是改爲 ARBolt, 即RBolt這個接口在繼承IBolt接口的同時,指定了參數類型,這時輸出如

java.lang.String java.lang.Boolean 

也就是說這個思路是能夠的,惟一的問題就是當實現目標接口的某一層接口,也是泛型時,直接定位到最底層,獲取的就是T,K這種符號參數了,由於實際的類型參數信息,在上一層定義的

那麼有沒有辦法將這個參數類型傳遞下去呢?

實際嘗試了一下,再往下走就比較複雜了,感受有點得不償失,不知道是否有相關的工具類

2. 繼承類方式

接口方式實現以後,繼承類方式也差很少了,並且相對而言會更簡單一點,由於繼承是單繼承的

@Test public void testGetAbsTypes() { Class basicClz = BEBolt.class; Type type; ParameterizedType ptype; while (true) { if (Object.class.equals(basicClz)) { break; } type = basicClz.getGenericSuperclass(); if (!(type instanceof ParameterizedType)) { basicClz = basicClz.getSuperclass(); continue; } ptype = (ParameterizedType) type; if (Object.class.equals(basicClz.getSuperclass().getSuperclass())) { // 若是ptype的父類爲Object,則直接分析這個 Type[] parTypes = ptype.getActualTypeArguments(); for (Type par : parTypes) { System.out.println(par.getTypeName()); } break; } else { basicClz = basicClz.getSuperclass(); } } } 

輸出以下,一樣有上面的問題

java.lang.String T 

III. 小結

經過反射方式,後去泛型類的參數信息,有幾個有意思的知識點:

  1. 獲取泛型類信息

    java.lang.Class#getGenericSuperclass
    java.lang.Class#getGenericInterfaces
    
    // 獲取實際的泛型參數 java.lang.reflect.ParameterizedType#getActualTypeArguments 
  2. Class判斷繼承關係

    java.lang.Class#isAssignableFrom
    // 父類做爲調用方,子類做爲參數 

II. 其餘

一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛

聲明

盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

掃描關注

 
相關文章
相關標籤/搜索