當(dāng)反射遇上泛型該如何處理

由于后面講到的反序列化器在反序列化List的時候需要確定泛型的type趁耗,所以這邊先講一下針對類型擦除的泛型串稀,我們要如何獲取其type

JAVA反射機制提供了運行時動態(tài)編程的可能

  • 在運行階段能夠隨意的獲取:
  • 任意一個類的所有屬性拆吆、函數(shù)與注解等信息;
  • 任意一個對象,都能夠調(diào)用它的任意屬性與函數(shù)蜓席。
  • 這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機制。
    眾所周知Java有個Object 類课锌,是所有Java 類的繼承根源厨内,其內(nèi)聲明了數(shù)個應(yīng)該在所有Java 類中被改寫的方法:hashCode()、equals()渺贤、clone()雏胃、
    toString()、getClass()等志鞍。其中g(shù)etClass()返回一個Class 對象瞭亮。
    Class是反射的起源。
public class Bean {
    private int i;
    public List<String> list;

    public Bean() {

    }

    public void setI(int i) {
        this.i = i;
    }

    public int getI() {
        return i;
    }
}

有類如上述雾。執(zhí)行下面代碼:

Class<Bean> beanClass = Bean.class;
        //獲得類以及父類中所有聲明為public的屬性
        System.out.println("所有public屬性:");
        for (Field field : beanClass.getFields()) {
            System.out.println(field.getName());
        }
        //獲得類(不包括父類)中所有的屬性
        System.out.println("所有屬性:");
        for (Field field : beanClass.getDeclaredFields()) {
            System.out.println(field.getName());
        }
        //獲得類以及父類中所有聲明為public的函數(shù)
        System.out.println("所有public函數(shù):");
        for (Method method : beanClass.getMethods()) {
            String methodName = method.getName();
            System.out.println(methodName);
        }

         //獲得類(不包括父類)中所有的函數(shù)
        System.out.println("所有函數(shù):");
        for (Method method : beanClass.getDeclaredMethods()) {
            String methodName = method.getName();
            System.out.println(methodName);
        }

很顯然街州,getDeclaredXX :會獲得Class中的所有內(nèi)容,getXX: 獲得當(dāng)前類以及父類的內(nèi)容,但是不包括非public
類中的屬性對應(yīng)反射中的Field玻孟,而函數(shù)則為Method唆缴。但是獲取構(gòu)造方法,則需要通過Constructor:

Constructor<?>[] constructors = beanClass.getConstructors();

操作屬性黍翎、調(diào)用函數(shù)的方法則需要編寫:

Method method = XX;
//在obj對象上調(diào)用函數(shù)
method.invoke(obj,...);

 Field field = XX;
//獲得obj中的屬性
field.get(obj);
//設(shè)置obj中的屬性
field.set(obj,value);

//調(diào)用構(gòu)造函數(shù)
Constructor<?>[] constructor = XX;
constructor.newInstance(...);

對于非public的Field/Method進行操作,需要先進行:setAccessible(true)
查看Bean的定義面徽,其中包括了List list成員,如何需要通過這個Field獲得對應(yīng)的泛型類型(即String)?
Java 泛型在運行的時候是會進行類型擦除匣掸,泛型信息只存在于代碼編譯階段趟紊。即對于List和List在運行階段都是List.class,泛型信息被擦除了。

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
assert(l1.getClass() == l2.getClass()); //結(jié)果為真

所以這時候沒法通過Class<?> type = Field.getType獲得需要的Class碰酝。好在Java為我們提供了 Type 接口霎匈,使用它我們可以得到這些信息。
Type是一個接口送爸,它的實現(xiàn)類有Class,子接口有 ParameterizedType, WildcardType等铛嘱。

ParameterizedType (參數(shù)化類型)

Type[] getActualTypeArguments(): 返回類型的參數(shù)的實際類型數(shù)組
如Map<String,Integer>的類型(Type)是屬于參數(shù)化類型(ParameterizedType),
則可以獲得:

Type key = getActualTypeArguments()[0];
Type value = getActualTypeArguments()[1];

其中Key即為String,Value則是Integer袭厂。

WildcardType (通配符的類型)

JAVA泛型通配符的使用規(guī)則:"PECS"

在泛型中可以通過通配符來聲明一個泛型類型墨吓,即使用?。同時可以指明?的上下邊界纹磺。帶上邊界的通配符:
List<? extends Number> list帖烘;帶下邊界的通配符:List<? super Integer> list。

getUpperBounds:獲得上邊界

對于List<? extends Number> list上邊界為Number橄杨,而List<? super Integer> list上邊界則為:Object秘症。

如果希望獲得某個類中List集合的泛型類型,則可以:

//指定獲得Bean中l(wèi)ist屬性
Field list = Bean.class.getField("list");
// 屬性對應(yīng)的Class如果是List或其子類
if (List.class.isAssignableFrom(list.getType())) {
    //獲得 Type
    Type genericType = list.getGenericType();
    //ParameterizedType
    if (genericType instanceof ParameterizedType) {
        //獲得泛型類型
        Type type = ((ParameterizedType) genericType).getActualTypeArguments()[0];
        //WildcardType  如果使用了通配符
        if (type instanceof WildcardType) {
            WildcardType wildcardType = (WildcardType) type;
            Type[] upperBounds = wildcardType.getUpperBounds();
            if (upperBounds.length == 1) {
                Type actualTypeArgument = upperBounds[0];
                System.out.println("獲得泛型上邊界類型:" + actualTypeArgument);
            }
        } else {
            System.out.println("獲得泛型類型:" + type);
        }
    } 
}

如果遇見一個泛型類

public class TypeReference<T> {
    protected TypeReference() {
        //獲取當(dāng)前對象的直接超類的 Type
        Type superClass = getClass().getGenericSuperclass();
        Type oriType = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }
}
new TypeReference<Object>(){}.getType()

在運行期可以通過反射機制獲取到Class對應(yīng)的泛型類型照卦。這里為什么沒有“類型擦除”?
觀察上面的類,如果將構(gòu)造函數(shù)的作用域改為public,那么會得到一個

java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType

如果將構(gòu)造函數(shù)改為public历极,則需要將類聲明為abstract才不會出現(xiàn)問題窄瘟。
這下子應(yīng)該明顯了衷佃,當(dāng)new這個類的時候趟卸,實際上是創(chuàng)建了一個匿名內(nèi)部類。TypeReference的子類氏义,泛型參數(shù)限定為Object锄列。
引入泛型擦除的原因是避免因為引入泛型而導(dǎo)致運行時創(chuàng)建不必要的類。Java 的泛型擦除是有范圍的惯悠,除了結(jié)構(gòu)化信息外的所有東西會擦除邻邮,與類及其字段和方法的類型參數(shù)相關(guān)的元數(shù)據(jù)都會被保留下來,可以通過反射獲取到克婶。
這里則是通過定義類(匿名內(nèi)部類)的方式筒严,在類信息中保留泛型信息,從而在運行時獲得這些泛型信息.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末情萤,一起剝皮案震驚了整個濱河市鸭蛙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筋岛,老刑警劉巖娶视,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異睁宰,居然都是意外死亡肪获,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門柒傻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孝赫,“玉大人,你說我怎么就攤上這事红符∏啾” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵违孝,是天一觀的道長刹前。 經(jīng)常有香客問我,道長雌桑,這世上最難降的妖魔是什么喇喉? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮校坑,結(jié)果婚禮上拣技,老公的妹妹穿的比我還像新娘千诬。我一直安慰自己,他們只是感情好膏斤,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布徐绑。 她就那樣靜靜地躺著,像睡著了一般莫辨。 火紅的嫁衣襯著肌膚如雪傲茄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天沮榜,我揣著相機與錄音盘榨,去河邊找鬼。 笑死蟆融,一個胖子當(dāng)著我的面吹牛草巡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播型酥,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼山憨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弥喉?” 一聲冷哼從身側(cè)響起郁竟,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎档桃,沒想到半個月后枪孩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡藻肄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年蔑舞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘹屯。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡攻询,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出州弟,到底是詐尸還是另有隱情钧栖,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布婆翔,位于F島的核電站拯杠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏啃奴。R本人自食惡果不足惜潭陪,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧依溯,春花似錦老厌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至慷嗜,卻和暖如春淀弹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洪添。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工垦页, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雀费,地道東北人干奢。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像盏袄,于是被迫代替她去往敵國和親忿峻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容