Tuesday, 17 September 2013

Accessing Generic Types At Runtime In Java

I was writing my n-th Dao implementation, this time using JPA.
I (and probably a whole lot of others) usually create a DAO per entity, parameterizing the entity type.
Specific DAO instances for entities implement the generic DAO using their entity type as type parameter. One generic DAO implementation exists, containing common operations like findById, persist, remove, etc. This generic DAO uses the class type specified by the implementing DAO classes (e.g. a Person) to manipulate or query the entity specified by this type. The (slightly) annoying problem for me has always been to instantiate that entity type in the generic DAO superclass. I’ve always done this by just creating a constructor in the generic DAO which takes a class argument containing the required Class of the entity. However, there’s a better way, which I’ll show in this post.
To start with an example, the generic DAO interface looks like this:
public interface GenericEntityDao {

   T findById(Serializable id);

   List findAll();

   ... more methods omitted
}
And its generic DAO implementation class looks something like this:
public abstract class GenericJpaDao implements GenericDao {

  private Class entityBeanType;

  @PersistenceContext
  private EntityManager entityManager;

  public T findById(Serializable id) {
     return entityManager.find(getEntityBeanType(), id);
  }

  public List findAll() {
      return entityManager.createQuery("from " + getEntityBeanType().getName() )
                          .getResultList();
  }

  protected Class getEntityBeanType() {
      return entityBeanType;
  }
  ... more methods omitted
 
}
The question is, how do we obtain the parametized type of T for the entityBeanType field to use in our queries? Earlier, I used to just instantiate this type in the constructor, like so:
public GenericJpaDao(Class entityBeanType) {
     this.entityBeanType = entityBeanType;
}

public JpaPersonDao extends GenericJpaEntityDao implements PersonDao {
   public PersonDao() {
      super(Person.class);
   }
}
But this is a bit silly, since we already know from the type parameter that the entity we’re interested in has type Person.
However, there is an easier way out: we can use Java’s ParameterizedType class to obtain information about the declared generic type. As stated in the javadoc:
/**
* ParameterizedType represents a parameterized type such as
* Collection<String>.
*
* A parameterized type is created the first time it is needed by a
* reflective method, as specified in this package. When a
* parameterized type p is created, the generic type declaration that
* p instantiates is resolved, and all type arguments of p are created
* recursively.

Thus, it seems that ParameterizedType contains the information that we want. How do we obtain an instance of it? The answer lies in Java’s getGenericSuperclass method, defined on the Class object. This returns a Type representing the superclass of this Class. The Javadoc of the method states the following:
* If the superclass is a parameterized type, the Type
* object returned must accurately reflect the actual type
* parameters used in the source code. The parameterized type
* representing the superclass is created if it had not been
* created before.


The Type interface itself is just a marker interface, containing no methods. Depending on the case at hand, an Type extending the Type interface will be returned. In our case, we’ll use the getGenericSuperclass method on a class extending our GenericJpaDao class. Thus, the superclass is JpaGenericDao, which is a parameterized type. In this case, the actual the Type object returned by getGenericSuperClass will be a ParameterizedType instance. This class contains a methodgetActualTypeArguments, which returns an array containing all generic type parameters used in the source code.
This is precisely what we desire, so in our GenericJpaDao class constructor, can get rid of the Class argument, and instead do the following:
  @SuppressWarnings("unchecked")
  public GenericJpaDao() {
    this.entityBeanType = ((Class) ((ParameterizedType) getClass()
        .getGenericSuperclass()).getActualTypeArguments()[0]);
  }
We know there’s precisely one type argument in our class, so we can take the first element of the array that is returned, which provides the Class of our entity. Thus we can get rid of all the annoying constructors with Class arguments, and handle determination of the entity type in just one place.
This may not provide the highest amount of code reduction you have ever experienced, but it’s a neat trick in any case.
Post a Comment