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