Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I resolve the actual type for a generic return type using reflection?

I have an interface with a method with a generic return type, and at runtime some instances of classes which indirectly implement that interface. Now I want to find out the actual return type for each implementation using reflection.

(My idea is to use this mechanism to define a strategy using an interface, and find a matching strategy (specific return type) out of a set of strategy implementations at runtime, without having to introduce redundant helper methods which expose the type).

To be more specific, let's consider following scenario:

private interface DAO <I extends Serializable, E> {

    public E getById (I id);
}

private abstract class AbstractDAO <T> implements DAO<Integer, T> {

    @Override
    public T getById (Integer id) {
        // dummy implementation, just for this example
        return null;
    }
}

private class PersonDAO extends AbstractDAO<Person> {
}

private class PersonDAOExtension extends PersonDAO {
}

At runtime, I want to find out for a given class (PersonDAOExtension.class) which type will be returned for the method getById(..) (expected to be: Person.class).

Using reflection, I can find out which generic Type is returned from this method. In this case, it's a TypeVariable (but could also be a Class, if any class in the hierarchy would specify a covariant return type):

Method method = PersonDAOExtension.class.getMethod("getById", Integer.class);
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof TypeVariable<?>) {
    TypeVariable<?> typeVariable = (TypeVariable<?>) genericReturnType;
    typeVariable.getName(); //results in "T"
}

I assume resolving the actual type would mean recursing into superclasses and interfaces and translate the raw (parameterizedType.getRawType()) and actual (parameterizedType.getActualTypeArguments()) type arguments for any parameterized type, until the desired type name is found.

Has anyone done this before, and maybe some code snippets ready which will help me achieve this? Many thanks in advance :)


Hint: I was able to extract following information at runtime using reflection, so the raw and actual type information is retained:

private abstract interface DAO<I, E>
private abstract class AbstractDAO<T> extends Object implements DAO<Integer, T> [raw type:DAO<I, E>]
private class PersonDAO extends AbstractDAO<Person> [raw type:AbstractDAO<T>]
private class PersonDAOExtension extends PersonDAO
like image 632
Peter Walser Avatar asked Jun 25 '13 12:06

Peter Walser


1 Answers

I was able to determine the generic return type of a method in one line using Google Guava's TypeToken class:

TypeToken.of(PersonDAOExtension.class)
        .resolveType(PersonDAOExtension.class.getMethod("getById", Integer.class).getGenericReturnType())
        .getRawType()

Alternatively, if you want to get the generic type of a class (as you did in your accepted answer), rather than the return type of a method, you could do the following:

TypeToken.of(PersonDAOExtension.class)
        .resolveType(AbstractDAO.class.getTypeParameters()[0])
        .getRawType()

Both of these solutions return Person.class as expected.

From your comments on the accepted answer, it looks like you just want to know whether the given DAO can accept Person objects. This too is doable with the API:

(new TypeToken<DAO<?, Person>>() {})
        .isSupertypeOf(TypeToken.of(PersonDAOExtension.class))

There is a decent explanation of the capabilities of this and other Guava reflection utilities on the Guava GitHub wiki.

like image 144
M. Justin Avatar answered Sep 23 '22 14:09

M. Justin