java泛型类型参数和对这些类型的操作

时间:2009-10-20 16:22:24

标签: java hibernate generics spring

在寻找我最近遇到的有趣情况的答案时,我遇到了以下问题: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();
}

谢谢!

2 个答案:

答案 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对象。