很早之前寫過利用泛型和反射機制抽象DAO ,對其中擷取子類泛型的class一直不是很理解。關鍵的地方是HibernateBaseDao的構造方法中的
Type genType = getClass().getGenericSuperclass();<br />Type[] params = ((ParameterizedType) genType).getActualTypeArguments();<br />entityClass = (Class)params[0];<br />
但是這個相對子類才會有用,而且那篇文章的HibernateBaseDao還不是abstract,如果一不小心執行個體化的話就會報異常。感覺java中通過反射取泛型的class還是挺複雜的,不過一旦取到的話就可以做很多事情了。
改進的例子和測試:
1.先定義介面(這裡寫的比較簡單就一個方法,具體可以再增加)
public interface BaseDao<T> {<br />T get(String id);</p><p>}
2.定義抽象類別實現介面
import java.lang.reflect.ParameterizedType;<br />import java.lang.reflect.Type;</p><p>public abstract class HibernateBaseDao<T> implements BaseDao<T> {<br />private Class<T> entityClass;</p><p>/**<br /> * 這個通常也是hibernate的取得子類class的方法<br /> *<br /> * @author "yangk"<br /> * @date 2010-4-11 下午01:51:28<br /> */<br />public HibernateBaseDao() {<br />Type genType = getClass().getGenericSuperclass();<br />Type[] params = ((ParameterizedType) genType).getActualTypeArguments();<br />entityClass = (Class) params[0];<br />}</p><p>@Override<br />public T get(String id) {<br />try {<br />return entityClass.newInstance();<br />} catch (InstantiationException e) {<br />// TODO Auto-generated catch block<br />e.printStackTrace();<br />} catch (IllegalAccessException e) {<br />// TODO Auto-generated catch block<br />e.printStackTrace();<br />}<br />return null;<br />}</p><p>}
子類的建構函式會調用父類的建構函式,所以當子類執行個體化的時候,父類的entityClass 已經得到了T.class。
3.定義一個entity
public class Entity {</p><p>private String name;</p><p>public String getName() {<br />return name;<br />}</p><p>public void setName(String name) {<br />this.name = name;<br />}<br />}
4.定義entity的DAO
public class EntityDao extends HibernateBaseDao<Entity> {</p><p>public void otherOperation() {</p><p>}</p><p>}
可以在裡面定義父類沒有的,針對具體子類特殊的方法。
5.測試
import static org.junit.Assert.assertNotNull;</p><p>import org.junit.After;<br />import org.junit.AfterClass;<br />import org.junit.Before;<br />import org.junit.BeforeClass;<br />import org.junit.Test;</p><p>public class JunitTest {<br />@BeforeClass<br />public static void setUpClass() throws Exception {<br />}</p><p>@AfterClass<br />public static void tearDownClass() throws Exception {<br />}</p><p>@Before<br />public void setUp() {<br />}</p><p>@After<br />public void tearDown() {<br />}</p><p>/**<br /> * Test of getEClass method, of class tmp.<br /> */<br />@Test<br />public void testNewClass() {<br />EntityDao testDao = new EntityDao();<br />Entity e = testDao.get(null);<br />assertNotNull(e);<br />}</p><p>}
運行,可以看到測試順利通過。
注意:上面子類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的方法。