Java 1.5在加入泛型支持时,为了保持兼容,采用的是擦除法实现,泛型的定义只在编译的时候有效,编译之后是没有保留泛型的类型信息的。所以,通常是无法在运行时获得泛型的类型活实例化泛型类的,如下面的代码都无法通过编译:
public
class
Test
<
T
>
{
public Test (){
T t = new T (); // error
Class < T > clazz = T . class ; //error
T [] ts = new T [ 10 ]; //error
}
}
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 >{
}
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来获取泛型的真正类型。
Jackson json中反序列化时,可以通过TypeReference传递容器中的泛型类型,也是利用了泛型继承的特例:
Map
<
Integer
,
ApkBean
>
map
=
mapper
.
readValue
(
jsonData
,
new
TypeReference
<
Map
<
Integer
,
ApkBean
>>(){});
TypeReference是一个泛型抽象类,在readValue的第二个方法中,传入了TypeReference的一个匿名子类实例,由此带入了Map的泛型信息。
除了泛型继承这种情况之外,还有另外两个特例,也可以获取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 ]);
}
}
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 ){
}
}
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中也比较少见。