在寻找我最近遇到的一个有趣情况的答案时,我遇到了以下问题:Type safety, Java generics and querying
我写了下面的类(清理了一下)
public abstract class BaseDaoImpl<T extends Serializable> extends HibernateDaoSupport implements BaseDao<T> {
/**
* Finds and Returns a list of persistent objects by a collection of criterions
* @param criterions
* @return list of persistent objects
* @throws DBException
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Collection<Criterion> criterions) throws DBException {
try {
DetachedCriteria criteria = DetachedCriteria.forClass(T.class); // BAD!!!
for (Criterion criterion : criterions) {
criteria.add(criterion);
}
List<T> result = getHibernateTemplate().findByCriteria(criteria);
return result;
}
catch (Exception e) {
throw new DBException(T.class + " lookup by " + criterions + " failed", e); // BAD!!!
}
}
}
由于某些(可能是充分的理由)T.class
导致编译时错误。
我的第一个问题是为什么?
如果我将其更改为 T.getClass()
,显然不应该编译 - 因为“扩展”或经历“删除”时没有“T” - 应该有一个静态方法,例如那。 eclipse IDE 给出以下编译消息:
Cannot make a static reference to the non-static method getClass() from the type Object
我的第二个问题是为什么?这个错误实际上意味着什么?
最后,按照上面链接中指定的方式(或者更确切地说是我的解释)解决这个问题是最佳方法吗?
public abstract class BaseDaoImpl<T extends Serializable> extends HibernateDaoSupport implements BaseDao<T>, MyGenericHelper<T> {
/**
* Finds and Returns a list of persistent objects by a collection of criterions
* @param criterions
* @return list of persistent objects
* @throws DBException
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Collection<MyCriterion> criterions) throws DBException {
try {
DetachedCriteria criteria = DetachedCriteria.forClass(getGenericClass()); // BAD!!!
for (Criterion criterion : criterions) {
criteria.add(criterion);
}
List<T> result = getHibernateTemplate().findByCriteria(criteria);
return result;
}
catch (Exception e) {
throw new DBException(getGenericClass() + " lookup by " + criterions + " failed", e); // BAD!!!
}
}
}
public interface MyGenericHelper<T extends Serializable> {
public Class<T> getGenericClass();
}
谢谢!
最佳答案
之所以无法访问T.class,是因为T在编译时被删除了,所以在运行时它并不存在来获取类。
解决这个问题的典型技巧是创建一个工厂方法:
public static <T extends Serializable> BaseDAOImpl<T> createBaseDAO(Class<T> klass) {
return new BaseDAOImpl<T>(klass);
}
然后在构造函数中将 klass 变量存储为字段,并在需要时引用它。
如果你想保留一个无参数构造函数,你可以使用你的接口(interface)(我个人只会使用 protected 抽象方法)。
protected abstract Class<T> getGenericClass();
编辑:对于通用抽象父类(super class),有几个选项。一种是拥有构造函数,然后让子类只需调用它(通过不使用无参数构造函数)。像这样的事情:
protected BaseDAOImpl(Class<T> klass) {
//store the parameter in a field.
}
那么静态方法不相关,因为您必须创建子类。当基于类可以返回正确的实现时,更多地使用静态工厂方法(这样你就有了一个工厂,而不仅仅是一个策略)。
为了完整起见,我应该指出,如果子类在扩展抽象类时声明泛型,如下所示:
public IntegerImpl extends BaseDAOImpl<Integer> {}
然后泛型类型被保留在类中。使用这个类有一个非常丑陋的技巧。我对此进行了实验,并且有效:
(Class<?>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]
但是它对继承层次结构做出了巨大的假设,如果可以避免的话,就不应该在任何严重的事情中使用它,但我将其包括在内是为了完整地了解正在发生的事情。
关于java泛型类型参数和这些类型的操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1595826/