My GenericDao has:
public T get(K id);
现在我的GenericDaoImpl有:
public T get(K id) {
return super.getHibernateTemplate().get(???, id);
}
我试着:
get(T.class, id);
但是我得到一个编译时错误:
cannot select from a type variable
是否有可能以某种方式正确地做到这一点?
由于类型擦除,这是不可能的。
需要以Class
实例为参数
您需要构建GenericDao,以便它能够找出它已被参数化的具体实体类型。例如,SomeEntityDaoImpl应该能够计算出它的实体类型是SomeEntity.class。
-
在GenericDao中添加一个抽象方法
getEntityClass
,并在每个DaoImpl中重写该方法以提供适当的实体类,或者 -
使用反射方法自动查找类型参数(我使用的是来自Google Guice的
MoreTypes.getGenericSupertype()
,其次是ParameterizedType.getActualTypeArguments()
)
那么您就不需要传递Class文字给get(),因为DaoImpl可以自己调用getEntityClass()。
public abstract class DAOImpl <T, PK extends Serializable> implements DAO<T, PK> {
protected abstract Class<T> getEntityClass();
public T findById(PK id){
return (T)getHibernateTemplate().get(getEntityClass(),id);
}
protected DetachedCriteria createDetachedCriteria(){
return DetachedCriteria.forClass(getEntityClass());
}
@SuppressWarnings("unchecked")
public List<T> findAll(){
return getHibernateTemplate().findByCriteria(createDetachedCriteria());
}
}
如果GenericDaoImpl
是抽象的,并且所有实际实现都有T
和K
的绑定,那么可以使用反射获得类型参数的值:
abstract class GenericDaoImpl<K, T> {
public T get(K id) {
Class<?> valueClazz = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return super.getHibernateTemplate().get(valueClazz, id);
}
}
class StringDao extends GenericDaoImpl<Integer, String> {
}
我在ideone上创建了一个稍微修改过的示例,但它只是打印类型类,在本例中为String。
这只适用于StringDao
(和所有其他dao)是GenericDaoImpl
的直接子类的情况,所以它有点脆弱。就像我说的,它将不工作,如果GenericDaoImpl不是抽象的,但意味着直接使用。在这种情况下,我认为你应该考虑一下你所做的是否是正确的方法。根据我的经验,这种通用的dao并不能很好地工作。