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:
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.
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:
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:
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:
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.