java 反射 子类泛型的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. public interface BaseDao<T> {
  2. T get(String id);
  3. }

2.定义抽象类实现接口

  1. import java.lang.reflect.ParameterizedType;
  2. import java.lang.reflect.Type;
  3. public abstract class 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. return null;
  28. }
  29. }

子类的构造函数会调用父类的构造函数,所以当子类实例化的时候,父类的entityClass 已经得到了T.class。

3.定义一个entity

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

4.定义entity的DAO

  1. public class EntityDao extends HibernateBaseDao<Entity> {
  2. public void otherOperation() {
  3. }
  4. }

可以在里面定义父类没有的,针对具体子类特殊的方法。

5.测试

  1. import static 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. public class JunitTest {
  8. @BeforeClass
  9. public static void setUpClass() throws Exception {
  10. }
  11. @AfterClass
  12. public static void tearDownClass() throws Exception {
  13. }
  14. @Before
  15. public void setUp() {
  16. }
  17. @After
  18. public void tearDown() {
  19. }
  20. /**
  21. * Test of getEClass method, of class tmp.
  22. */
  23. @Test
  24. public void 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的方法。

上一篇:SQL复习四(完整性约束)


下一篇:结合java的反射和泛型性质简化JDBC和相应的同步等服务器数据库操作代码