在运行时获取泛型的类型

  • Post author:
  • Post category:其他


转自:

http://www.dongliu.net/post/5896921377931264




都Java 1.5在加入泛型支持时,为了保持兼容,采用的是擦除法实现,泛型的定义只在编译的时候有效,编译之后是没有保留泛型的类型信息的,运行时所有的instance都当作限界类型来使用,没有指定限界类型是则是当作Object类型。所以,通常是无法在运行时获得泛型的类型活实例化泛型类的,如下面的代码都无法通过编译:


public


class


Test


<


T


>


{




public


Test


(){




T


t


=


new


T


();


// error



Class


<


T


>


clazz


=


T


.


class


;


//error



T


[]


ts


=


new


T


[


10


];


//error



}



}


然而,擦除法的实现存在一些特列,在这些特例情况下,Java会记录泛型的类型信息,并且可以通过Api来获取的。比如在各种持久化框架中广泛使用的BaseDao泛型类的写法,就利用一个泛型继承的特例:


public


class


BaseDAO


<


U


>


{




private


Class


<


U


>


entityClass


;


protected


BaseDAO


()


{




// 获取泛型类型



Type


type


=


getClass


().


getGenericSuperclass


();



Type


[]


trueType


=


((


ParameterizedType


)


type


).


getActualTypeArguments


();



this


.


entityClass


=


(


Class


<


U


>)


trueType


[


0


];



}


public


U


getById


(


Serializable


id


){




PersistenceManager


pm


=


getPersistenceManager


();



pm


.


setDetachAllOnCommit


(


true


);



U


object


;



try


{




object


=


pm


.


getObjectById


(


entityClass


,


id


);



}


catch


(


JDOObjectNotFoundException


e


)


{




object


=


null


;



}


finally


{




pm


.


close


();



}



return


object


;



}



}


public


class


TagDAO


extends


BaseDAO


<


Tag


>{




}


代码的关键是getGenericSuperclass方法,这个方法返回的类型为ParameterizedType,里面可以通过getActualTypeArguments来获取泛型的真正类型。这样的特例是违背了擦除法的实现思想的,这也是Java为保证类型系统完整而不得不做的妥协。






Jackson json中反序列化时,可以通过TypeReference传递容器中的泛型类型,也是利用了泛型继承的特例:



Map


<


Integer


,


ApkBean


>


map


=


mapper


.


readValue


(


jsonData


,


new


TypeReference


<


Map


<


Integer


,


ApkBean


>>(){});


TypeReference是一个泛型抽象类,在readValue的第二个方法中,传入了TypeReference的一个匿名子类实例,由此带入了Map的泛型信息。由于是两级泛型的嵌套,具体的情况其实更复杂一些,有兴趣的可以看看Jackson代码中TypeReference和TypeFactory的实现。






除了泛型继承这种情况之外,还有另外两个特例,也可以获取ParameterizedType类型,继而获取到泛型的类型。一个是类的field可以通过getGenericType来获取:



public


class


Test


{


private


Map


<


String


,


Number


>


map


;


public


static


void


main


(


String


[]


args


)


throws


NoSuchFieldException


{




Class


<?>


clazz


=


Test


.


class


;



Field


field


=


clazz


.


getDeclaredField


(


“map”


);



//取得泛型类型



Type


type


=


field


.


getGenericType


();



ParameterizedType


ptype


=


(


ParameterizedType


)


type


;



System


.


out


.


println


(


ptype


.


getActualTypeArguments


()[


0


]);



System


.


out


.


println


(


ptype


.


getActualTypeArguments


()[


1


]);



}


}


另外就是方法的泛型参数可以通过getGenericParameterTypes来获取:



public


class


Test


{


public


static


void


main


(


String


[]


args


)


throws


NoSuchMethodException


{




Class


<?>


clazz


=


Test


.


class


;



Method


method


=


clazz


.


getDeclaredMethod


(


“getGenericSample”


,


Collection


.


class


);



//取得泛型类型参数集



Type


[]


type


=


method


.


getGenericParameterTypes


();



ParameterizedType


ptype


=


(


ParameterizedType


)


type


[


0


];



type


=


ptype


.


getActualTypeArguments


();



System


.


out


.


println


(


type


[


0


]);



}


public


void


getGenericSample


(


Collection


<


Number


>


collection


){


}



}

这两种特例的实用价值比较小,实际的代码hack中也比较少见。