java泛型类型参数和对这些类型的操作 [英] java generics type parameters and operations on those types

查看:172
本文介绍了java泛型类型参数和对这些类型的操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在寻找一个我最近遇到的有趣情况的答案时,我遇到了以下问题:



我写了下面的类(清理了一下)

  public abstract class BaseDaoImpl< T extends Serializable>扩展HibernateDaoSupport实现BaseDao< T> {
$ b $ / **
*通过一系列标准查找并返回一个持久对象列表
* @param标准符号
* @return持久对象列表
* @throws DBException
* /
@SuppressWarnings(unchecked)
protected List< T> findByCriteria(Collection< Criterion> Criterions)抛出DBException {
try {
DetachedCriteria criteria = DetachedCriteria.forClass(T.class); //坏! (标准标准:标准)
{
criteria.add(标准);
}

列表< T> result = getHibernateTemplate()。findByCriteria(criteria);
返回结果;
}
catch(Exception e){
throw new DBException(T.class +lookup by+ criterions +failed,e); //坏!





$ b $ p $对于某些(可能是很好的理由) T.class 导致编译时错误。



我的第一个问题是为什么?



如果我将它改为 T.getClass()显然不应该编译 - 因为当expanded或者通过擦除 - 应该有一个这样的静态方法。 Eclipse IDE提供了以下编译信息:

lockquote

无法对
非静态方法getClass()从
类型对象


我的第二个问题是为什么?这个错误实际上暗示了什么?



最后,以上述链接中指定的方式解决这个问题(或者说我的解释)是最优方法吗? / p>

  public abstract class BaseDaoImpl< T extends Serializable>扩展HibernateDaoSupport实现BaseDao< T>,MyGenericHelper< T> {
$ b $ / **
*通过一系列标准查找并返回一个持久对象列表
* @param标准符号
* @return持久对象列表
* @throws DBException
* /
@SuppressWarnings(unchecked)
protected List< T> findByCriteria(Collection< MyCriterion>标准)抛出DBException {
try {
DetachedCriteria criteria = DetachedCriteria.forClass(getGenericClass()); //坏! (标准标准:标准)
{
criteria.add(标准);
}

列表< T> result = getHibernateTemplate()。findByCriteria(criteria);
返回结果;
}
catch(Exception e){
throw new DBException(getGenericClass()+lookup by+ criterions +failed,e); //坏!
}
}
}

public interface MyGenericHelper< T extends Serializable> {
public Class< T> getGenericClass();

$ / code>

谢谢!

解决方案

T.class无法访问的原因是因为T在编译时被擦除,所以它在运行时不存在以获得类。



典型的黑客攻击方法是制作一个工厂方法:

  public static 返回新的BaseDAOImpl< T>(klass); 





$ b然后在构造函数中将klass变量存储为一个字段并引用它你需要它。



如果你想保留一个没有参数的构造函数,你可以使用你的接口(我个人只需要用受保护的抽象方法)。 >

 受保护的抽象类< T> getGenericClass(); 

编辑:在通用抽象超类的情况下,有几个选项。一个是有构造函数,然后让子类必须调用它(通过没有无参数构造函数)。像这样的东西:

pre $ protected BaseDAOImpl(Class< T> klass){
//将参数存储在字段。
}

然后,静态方法并不相关,因为您必须创建子类。静态工厂方法在基于类可以返回正确的实现时更多地使用(所以你有一个工厂,而不仅仅是一个策略)。



为了完整性,我应该指出,如果子类在扩展抽象类时声明泛型,如下所示:

  public IntegerImpl extends BaseDAOImpl< ;整数> {} 

然后通用类型保存在类中。使用这个类有一个非常丑陋的破解。我对此进行了实验,结果如下:

 (Class<?>)((ParameterizedType)this.getClass()。 getActualTypeArguments()[0] 

但是它对继承层次做了大量的假设,如果完全可以避免,不应该用在任何严肃的事情上,但为了全面了解发生了什么,我将它包括在内。


In searching for an answer to an interesting situation which I had recently encountered I came upon the following question: Type safety, Java generics and querying

I have written the following class (cleaned up a bit)

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!!!
        }
    }
}

For some (probably good reason) T.class causes a compile time error.

My first question is why?

If I change it to T.getClass() which obviously shouldn't compile - because no 'T' when "expanded" or goes through "erasure" - should have a static method such as that. The eclipse IDE gives the following compilation message:

Cannot make a static reference to the non-static method getClass() from the type Object

My second question is why? And what does this error imply actually?

Finally, would solving this in the manner specified in the link above (or rather my interpretation of) be the most optimal way?

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();
}

Thanks!

解决方案

The reason why T.class can't be accessed is because T is erased at compile time, so it doesn't exist at runtime to get a class.

The typical hack around this is to make a factory method:

 public static <T extends Serializable> BaseDAOImpl<T> createBaseDAO(Class<T> klass) {
       return new BaseDAOImpl<T>(klass);
 }

And then in the constructor store the klass variable as a field and reference it when you need it.

You could use your interface (personally I would just go with a protected abstract method) if you want to keep a no-arg constructor.

 protected abstract Class<T> getGenericClass();

EDIT: In the case of the generic abstract superclass, there are a couple of options. One is have the constructor and then have the subclasses just have to call it (by not having a no-arg constructor). Something like this:

  protected BaseDAOImpl(Class<T> klass) {
       //store the parameter in a field.
  }

Then the static method isn't relevant, as you have to create the subclass. The static factory method is more used when based on the class you can return the right implementation (so you have a factory, not just a strategy).

For the sake of completeness, I should point out that if the subclasses declare the generic when they extend the abstract class like this:

 public IntegerImpl extends BaseDAOImpl<Integer> {}

then the generic type is preserved in the class. There is a really ugly hack to get at this using the class. I experimented with this, and it worked:

  (Class<?>) ((ParameterizedType)  this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]

But it makes huge assumptions about the inheritance hierarchy, and should not be used in anything serious if it can at all be avoided, but I'm including it for the sake of a complete picture of what is going on.

这篇关于java泛型类型参数和对这些类型的操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆