天天看點

java 反射 泛型機制 獲得泛型的實際類型(一)

java運用反射機制能夠讀取和調用加載到記憶體中的java程式(Class,Field,Method,Array等)。但是,如果屬性或方法使用了泛型來聲明資料類型,那麼能否讀取java程式所指定的泛型具體是什麼呢?

在網上看到過一個牛人在百度知道裡對一個人的回答,他的意思大概是泛型隻在編譯的時候用于類型的檢查,java程式加載進記憶體後,泛型就不存在了,而jdk的反射是模拟java運作時的環境讀取和調用程式,是以,不能獲得泛型的實際類型。牛人告誡道不能把泛型的功能想得太強大了,如果要獲得泛型的實際類型,隻有在運作時泛型被指定後才能讀取。例如如下方法可以讀取泛型執行個體化之後具體是什麼類型的,但是該方法隻有在java類被執行個體化之後才能調用。

public class GenericClass<T extends List<Number> >{

private T t;

private List<Contract> contracts;

public Class getActualClass(){

return t.getClass();

}

}

牛人的觀點很有道理。再例如,我們在用反射讀取調用getDeclaraedMethod的時候,傳入的參數類型清單就不能使用泛型。

但是,有時候,比如屬性 private List<Contract> contracts;我們就想知道List所指定的泛型是什麼,然後才能給contracts屬性設定一個vlalue。這時候該怎樣讀取屬性呢?

java的反射機制還有一套Type接口,該接口的實作類和子接口向我們展示了java的泛型在運作時是怎麼實作的。通路這些接口可以在運作時讀取關于泛型的資料,是以也不能說泛型在運作時就沒有了,而是交給其他對象來處理了。

Class類直接實作了Type接口,但是Type的實作類不止一個,換句話說,Class僅僅是Type的一種,如果我們在運作時通過 type instanceof Class 傳回true 則可以将type類型強轉為Class執行個體,即 Class cla = (Class)type; 然後就可以用該Class執行個體構造對象調用方法了。換言之,泛型是class之外的Type,我們隻要用反射通路各種Type,直到某個Type被驗證是Class類型的,那我們就能找到我們想要的運作時Class了。

那麼Class之外,還有有哪些Type呢?這就是我之前一直沒有看懂的那些反射api了内容了。

泛型的英文是GenericType 方法的泛型參數是method.getGenericParameterTpe 屬性的泛型是 field.getGenericType 這二者都傳回Type,不同的僅僅是方法傳回的所有參數的Type[],而屬性僅僅傳回一個Type。此時的type不僅帶有Class資訊還帶有<>裡的資料。但是jdk卻并沒有為Type規定任何方法,唯一的辦法是檢查Type究竟是哪種子Type種執行個體,直到找到Class類型的執行個體為止。是以,要獲得泛型的實際類型,要麼是遞歸調用方法,要麼是用while循環。

區分泛型聲明,使用泛型時傳遞類型參數,以及引用泛型這三者所指是很有意義的。這三者的差別就相當于是 方法聲明的形式參數,調用方法傳遞實質參數,以及在方法體中用參數名使用某個參數的差別。以上例的GenericClass來說,類名後的T是泛型聲明,屬性使用的T是泛型引用。而new GenericClAass<Contract> 則時給泛型傳遞類型參數。GenericClassn内還有List<Number> 這是對給List接口聲明的泛型傳遞類型參數,換言之用在class泛型聲明<>裡内的内容并不都是泛型聲明,泛型聲明可以組合其他類型描述自己。

了解了以上的差別,再去看java反射包裡那些各種type接口,就省事多了。

還有一點是 泛型中 <>内的 extends包括了implements

一,TypeVariable Type的子接口 實作類是TypeVariableImpl,這個是運作時用來處理泛型聲明的。通過 type instenceof TypeVariable 傳回true 然後 type = (TypeVariable)type 便可通路該接口提供的三個方法.也可以通過type.getClass() == TypeVariableImpl.class來判斷,但是不推薦,因為實作類可能更新。

1,getName():String 傳回形式泛型參數的名字 就是T E K 等。

2,Type[] getBounds() 傳回泛型聲明的上邊界類型。如果沒有指定 T extends什麼,那傳回的Type 就是 Object.class。注意1,該方法并不一定傳回Class執行個體,因為泛型的使用可以很複雜,一個泛型聲明extedns的對象完全可能又是一個泛型。2,該方法雖然傳回的是Type數組,但是經過筆者測試,該方法總是傳回一個元素。筆者猜想這或許是考慮到可以傳回空數組是以避免傳回null(ps:不确定,待高人回答)。

3,隻有類(Class) 構造方法(Constractor),方法(Method)才能聲明泛型,這三者都是GenericDeclaration的實作類。是以TypeVariable<D extends GenericDeclaration>隻能接收GenericDeclarationd的類型參數。 D getGenericDeclaration() 傳回聲明泛型的對象 即某個Class,Constractor或Method