With Hibernate and generic DAO it has now become a common practice to figure out the class of generic type at runtime (please note that the code is a bit simplified):
import java.lang.reflect.ParameterizedType;
// It's not marked as abstract, probably better name would be GenericDao...
public class AbstractDao<T> {
private Class<T> elementClass;
@SuppressWarnings("unchecked")
public AbstractDao() {
elementClass =
(Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
public Class<T> getElementClass() {
return elementClass;
}
...
}
Then we can do things like:
public class ConcreteDao extends AbstractDao<SomeEntity> {
}
ConcreteDao dao = new ConcreteDao();
This approach is very nice, since we don’t have to pass an entity class during initialization. But it has some limitations, one of which is that it won’t work if we try to initialize it directly, e.g.:
// java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
AbstractDao<SomeEntity> dao = new AbstractDao<SomeEntity>();
Due to type erasure it is not possible to figure out the generic type for stand alone generic class, e.g. AbstractDao. This can be done only for superclass. In this case the superclass is Object, so sorry, no go.
For this case we can add a remedy – another constructor with a Class parameter, then we can initialize the class like this:
AbstractDao<SomeEntity> dao = new AbstractDao<SomeEntity>(SomeEntity.class);
Also since we are trying to getGenericSuperclass, this code will work only with single inheritance, multiple inheritance won’t work:
public class ConcreteDao extends AbstractDao<SomeEntity> {
}
class SpecializedDao extends ConcreteDao {
}
SpecilizedDao dao = new SpecializedDao(); // no go, since constructor tries to read generic info from ConcreteDao
After some thinking I’ve made it a little bit more flexible (but in a somewhat hacky way):
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class AbstractDao<T> {
private Class<T> elementClass;
public AbstractDao(final Class<T> clazz) {
elementClass = clazz;
}
@SuppressWarnings("unchecked")
protected AbstractDao() {
Class<?> cl = getClass();
if (Object.class.getSimpleName().equals(cl.getSuperclass().getSimpleName())) {
throw new IllegalArgumentException(
"Default constructor does not support direct instantiation");
}
while (!AbstractDao.class.getSimpleName().equals(cl.getSuperclass().getSimpleName())) {
// case of multiple inheritance, we are trying to get the first available generic info
if (cl.getGenericSuperclass() instanceof ParameterizedType) {
break;
}
cl = cl.getSuperclass();
}
if (cl.getGenericSuperclass() instanceof ParameterizedType) {
elementClass =
(Class<T>) ((ParameterizedType) cl.getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
public Class<T> getElementClass() {
return elementClass;
}
...
}
Now we can do things like:
public class ConcreteDao extends AbstractDao<SomeEntity> {
}
class SpecializedDao extends ConcreteDao {
}
SpecilizedDao dao = new SpecializedDao();
Like this:
One blogger likes this post.