在运行时获取泛型的类型

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
     }
}

然而,擦除法的实现存在一些特列,在这些特例情况下,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来获取泛型的真正类型。

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 ]);
     }

}

另外就是方法的泛型参数可以通过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中也比较少见。

猜你喜欢

转载自blog.csdn.net/qq_21955179/article/details/49177971