1. 程式人生 > >java泛型T.class的獲取

java泛型T.class的獲取

   很早之前寫過利用泛型和反射機制抽象DAO ,對其中獲取子類泛型的class一直不是很理解。關鍵的地方是HibernateBaseDao的構造方法中的

  1. Type genType = getClass().getGenericSuperclass();   
  2. Type[] params = ((ParameterizedType) genType).getActualTypeArguments();   
  3. entityClass =  (Class)params[0];   

但是這個相對子類才會有用,而且那篇文章的HibernateBaseDao還不是abstract,如果一不小心例項化的話就會報異常。感覺java中通過反射取泛型的class還是挺複雜的,不過一旦取到的話就可以做很多事情了。

改進的例子和測試:

1.先定義介面(這裡寫的比較簡單就一個方法,具體可以再增加)

  1. publicinterface BaseDao<T> {  
  2.     T get(String id);  
  3. }  

2.定義抽象類實現介面

  1. import java.lang.reflect.ParameterizedType;  
  2. import java.lang.reflect.Type;  
  3. publicabstractclass HibernateBaseDao<T> implements BaseDao<T> {  
  4.     private
     Class<T> entityClass;  
  5.     /** 
  6.      * 這個通常也是hibernate的取得子類class的方法 
  7.      *  
  8.      * @author "yangk" 
  9.      * @date 2010-4-11 下午01:51:28 
  10.      */
  11.     public HibernateBaseDao() {  
  12.         Type genType = getClass().getGenericSuperclass();  
  13.         Type[] params = ((ParameterizedType) genType).getActualTypeArguments();  
  14.         entityClass = (Class) params[0];  
  15.     }  
  16.     @Override
  17.     public T get(String id) {  
  18.         try {  
  19.             return entityClass.newInstance();  
  20.         } catch (InstantiationException e) {  
  21.             // TODO Auto-generated catch block
  22.             e.printStackTrace();  
  23.         } catch (IllegalAccessException e) {  
  24.             // TODO Auto-generated catch block
  25.             e.printStackTrace();  
  26.         }  
  27.         returnnull;  
  28.     }  
  29. }  

子類的建構函式會呼叫父類的建構函式,所以當子類例項化的時候,父類的entityClass 已經得到了T.class。

3.定義一個entity

  1. publicclass Entity {  
  2.     private String name;  
  3.     public String getName() {  
  4.         return name;  
  5.     }  
  6.     publicvoid setName(String name) {  
  7.         this.name = name;  
  8.     }  
  9. }  

4.定義entity的DAO

  1. publicclass EntityDao extends HibernateBaseDao<Entity> {  
  2.     publicvoid otherOperation() {  
  3.     }  
  4. }  

可以在裡面定義父類沒有的,針對具體子類特殊的方法。

5.測試

  1. importstatic org.junit.Assert.assertNotNull;  
  2. import org.junit.After;  
  3. import org.junit.AfterClass;  
  4. import org.junit.Before;  
  5. import org.junit.BeforeClass;  
  6. import org.junit.Test;  
  7. publicclass JunitTest {  
  8.     @BeforeClass
  9.     publicstaticvoid setUpClass() throws Exception {  
  10.     }  
  11.     @AfterClass
  12.     publicstaticvoid tearDownClass() throws Exception {  
  13.     }  
  14.     @Before
  15.     publicvoid setUp() {  
  16.     }  
  17.     @After
  18.     publicvoid tearDown() {  
  19.     }  
  20.     /** 
  21.      * Test of getEClass method, of class tmp. 
  22.      */
  23.     @Test
  24.     publicvoid testNewClass() {  
  25.         EntityDao testDao = new EntityDao();  
  26.         Entity e = testDao.get(null);  
  27.         assertNotNull(e);  
  28.     }  
  29. }  

執行,可以看到測試順利通過。

注意:上面子類DAO的寫法public class EntityDao extends HibernateBaseDao<Entity>

一定要在父類後面帶上泛型,負責編譯就會出錯。

 附:這是泛型擦拭法使得Generic無法獲取自己的Generic Type型別。實際上BadClass<String>()例項化以後Class裡面就不包括T的資訊了,對於Class而言T已經被擦拭為Object,而真正的T引數被轉到使用T的方法(或者變數宣告或者其它使用T的地方)裡面(如果沒有那就沒有存根),所以無法反射到T的具體類別,也就無法得到T.class。而getGenericSuperclass()是Generic繼承的特例,對於這種情況子類會儲存父類的Generic引數型別,返回一個ParameterizedType,這時可以獲取到父類的T.class了,這也正是子類確定應該繼承什麼T的方法。