Another nifty trick I hadn't seen before which I ran across on the Hibernate page for their generic DAO classes:
public abstract class GenericHibernateDAO<T, ID extends Serializable>
implements GenericDAO<T, ID> {
private Class<T> persistentClass;
private Session session;
public GenericHibernateDAO() {
this.persistentClass = (Class<T>)
((ParameterizedType) getClass()
.getGenericSuperclass())
.getActualTypeArguments()[0];
}
// ...
} Playing around with this revealed two things to me:
- This trick only works for concrete subclasses.
- The concrete subclass needs to not be generic, at least for the parameter in the interesting expression above.
So one could, for example, create a generic factory using this technique, although there does not seem on the surface to be much advantage:
public abstract class FactoryBase<T> {
private final Class<T> type = (Class)
((ParameterizedType) getClass()
.getGenericSuperclass())
.getActualTypeArguments()[0];
protected FactoryBase() { }
public Class<T> getType() { return type; }
public T create()
throws IllegalAccessException, InstantiationException {
// Real implementations do something more interesting
// than imitate "new T()".
return type.newInstance();
}
}
public class FredFactory
extends FactoryBase<Fred> { }
public class Main {
public static void main(final String arguments) {
final Fred fred = new FredFactory().create();
}
} How different is this from supplying a Class literal to the factory consturctor?
getGenericSuperclass() seems one of those "back pocket" techniques: something I'll keep handy if the need arises but which I don't go out of my way to use.
UPDATE: A little playing shows that the minimal requirement is that the generic parameter whose index is queried with getActualTypeArguments() must be concrete; it is ok for another parameter to be a generic:
public class Bob<T, U> {
final Class<T> tClass
= getGenericTypeByIndex(getClass(), 0);
private static Class getGenericTypeByIndex(
final Class clazz, final int index) {
return (Class) ((ParameterizedType) clazz
.getGenericSuperClass())
.getActualTypeArguments()[index];
}
}
} This snippet works ok as:
public class Fred<U> extends Bob<Fred, U> {
public static void main(final String arguments) {
new Fred<String>();
}
} But fails at runtime with a ClassCastException trying to turn a sun.reflect.generics.reflectiveObjects.TypeVariableImpl into a java.lang.Class:
public class Sally<T, U> extends Bob<T, U> {
public static void main(final String arguments) {
new Sally<Sally, String>();
}
}
1 comment:
Strangely, I updated my Mustang from b90 to b93 and the final example stopped working.
Post a Comment