在寻找我最近遇到的有趣情况的答案时,我遇到了以下问题: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提供以下编译消息:
无法进行静态引用 非静态方法getClass()来自 类型对象
我的第二个问题是为什么?这个错误究竟意味着什么?
最后,以上述链接中指定的方式解决这个问题(或者更确切地说,我的解释)是最优化的方式吗?
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();
}
谢谢!
答案 0 :(得分:6)
无法访问T.class的原因是因为T在编译时被擦除,因此在运行时不存在以获取类。
典型的解决方法是制作工厂方法:
public static <T extends Serializable> BaseDAOImpl<T> createBaseDAO(Class<T> klass) {
return new BaseDAOImpl<T>(klass);
}
然后在构造函数中将klass变量存储为字段,并在需要时引用它。
如果你想保留一个无参数的构造函数,你可以使用你的界面(我个人会使用受保护的抽象方法)。
protected abstract Class<T> getGenericClass();
编辑:在通用抽象超类的情况下,有几个选项。一个是构造函数,然后让子类只需要调用它(没有一个无参数的构造函数)。像这样:
protected BaseDAOImpl(Class<T> klass) {
//store the parameter in a field.
}
然后静态方法不相关,因为您必须创建子类。基于类可以返回正确的实现时更多地使用静态工厂方法(因此您有工厂,而不仅仅是策略)。
为了完整起见,我应该指出,如果子类在扩展抽象类时声明泛型:
public IntegerImpl extends BaseDAOImpl<Integer> {}
然后在类中保留泛型类型。使用该课程可以获得非常难看的黑客攻击。我尝试了这个,它起作用了:
(Class<?>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]
但它对继承层次结构做出了巨大的假设,如果它完全可以避免的话,不应该用于任何严重的事情,但是为了全面了解正在发生的事情,我将其包括在内。
答案 1 :(得分:1)
对于某些人(可能有充分理由) T.class导致编译时错误。
我的第一个问题是为什么?
在编译时,编译器不知道类型T(在这种情况下,它可以是Integer,String),不能支持T.class返回实际的类。
但是,在运行时,由于type erasure
而删除了类型信息Cannot make a static reference to the non-static method getClass() from
类型对象
我的第二个问题是为什么?什么 这个错误实际上意味着什么?
Hm .. getClass()是类(非静态)成员方法,它需要一个对象。 T不是一个对象,它是一个类型,所以它失败了。它们不会使它静态,因为已经有一个XXX.class关键字,您可以从中获取Class对象。