1

Given a Type instance (which may be a Class or ParameterizedType), I need to get the specific Type of an interface implemented by the class. Without generics this is easy, call getInterfaces() on a Class instance and you're done. However, I need this to work even when the implemented interface has its own type parameters that may or may not depend on the type parameters of the original class itself.

Some examples, the function should return Iterable<Integer> when given a class

class Foo implements Iterable<Integer> {}

but must also return Iterable<String> given the class

class Bar<T> implements Iterable<T> {}

and a ParmeterizedType representing Bar<String>

Is there an easy way to do this with built in reflection, third party tools etc?

To clarify, this needs to work not only with type instances retrieved via literals (Foo.class etc), but also those returned via reflection that can contain applied type parameters, for example the return type returned via reflection from the method

static Bar<String> magic() { ... }

This would be a ParameterizedType referencing the raw Bar class and the String type argument.

4 Answers 4

2

You should probably have a look at the Google Guava TypeToken class: http://code.google.com/p/guava-libraries/wiki/ReflectionExplained

It offers sophisticated mechanisms for resolving types in various contexts. And if I understood your question correctly, then something like the TypeToken#resolveType(Type) method might be close to what you are looking for:

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Iterator;

import com.google.common.reflect.TypeToken;

class Foo implements Iterable<Integer> {

    @Override
    public Iterator<Integer> iterator()
    {
        return null;
    }
}

class Bar<T> implements Iterable<T> {

    @Override
    public Iterator<T> iterator()
    {
        return null;
    }
}

public class TypeParameterTest
{
    public static void main(String[] args) throws Exception
    {
        Type i0 = getInterface(Foo.class, 0);
        System.out.println("First interface implemented by Foo: "+i0);

        Method method = TypeParameterTest.class.getDeclaredMethod("magic");
        Type returnType = method.getGenericReturnType();

        System.out.println("Magic method return type: "+returnType);

        Type i1 = getInterface(returnType, 0);
        System.out.println("First interface implemented by Bar<String>: "+i1);
    }

    private static Type getInterface(Type type, int interfaceIndex)
    {
        TypeToken<?> typeToken = TypeToken.of(type);
        Class<?> c = typeToken.getRawType();
        Type[] interfaces = c.getGenericInterfaces();
        if (interfaces.length == 0)
        {
            return null;
        }
        Type i = interfaces[interfaceIndex];
        return typeToken.resolveType(i).getType();
    }

    public static Bar<String> magic() { return null; }
}

The output here is

First interface implemented by Foo: java.lang.Iterable<java.lang.Integer>
Magic method return type: Bar<java.lang.String>
First interface implemented by Bar<String>: java.lang.Iterable<java.lang.String>
Sign up to request clarification or add additional context in comments.

1 Comment

I believe this is exactly what I need, yes! Would give 10 up votes if I could :)
0

You can use reflections when the type is hard coded because it subclasses a specific type. Here the type is stored in the class information.

If you use reflections, you can see that Foo extends Iterable<String> but if you have a Bar<String> this is just a Bar at runtime due to type erasure and all you see is that Bar extends Iterable<T>

If you have a generic type which is parameterised, this is not recorded anywhere as it has no action at runtime. If you need this type, you need save it as an additional field.

e.g. There is a class Collections.CheckedMap which checks the types at runtime and it starts like this

private static class CheckedMap<K,V>
    implements Map<K,V>, Serializable
{
    private static final long serialVersionUID = 5742860141034234728L;

    private final Map<K, V> m;
    final Class<K> keyType;
    final Class<V> valueType;

3 Comments

This is simply not true. I am using reflection to read the return type of a method which will contain the complete type, including the applied type parameters, in the form of a ParameterizedType.
@SoftMemes You can only read types which are burnt into the class information, not types which are created at runtime.
Peter wrote that is not recommend so it it possible, but that is not the case with reflection. Beside what is the difference when you pass the generic arguments and method arguments ?
0

Here is an example of using ParameterizedType

import java.lang.reflect.*;

public class Foo {

  static class Bar<T> {

  }
  static class SubBar extends Bar<Integer> {

  }

  public static void main(String argv[]) {

    ParameterizedType pt = (ParameterizedType)SubBar.class.getGenericSuperclass();
    Type[] t = pt.getActualTypeArguments();

    for (int i=0;i<t.length;i++) {

       System.out.println(t[i]);
    }
  }
}

3 Comments

Two issues with this. First of all it does base classes rather than interfaces, for my case I need an interface. Second of all it wouldn't give me the correct answer when SubBar also have type parameters passed on (potentially inside other types).
Type[] genericInterfaces = BarFoo.class.getGenericInterfaces(); for (Type genericInterface : genericInterfaces) { if (genericInterface instanceof ParameterizedType) { Type[] genericTypes = ((ParameterizedType) genericInterface).getActualTypeArguments(); for (Type genericType : genericTypes) { System.out.println("Generic type: " + genericType); } }
Still not actually applying the type parameters in the case where they are passed on. This would end up with an unresolved TypeVariable.
0
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;

public class Dummy implements Iterable<Integer> {

    public static void main(String args[]) throws Exception {

        Dummy d = new Dummy();

        Type[] genericInterfaces = Dummy.class.getGenericInterfaces();

        for (Type genericInterface : genericInterfaces) {
            if (genericInterface instanceof ParameterizedType) {
                Type[] genericTypes = ((ParameterizedType) genericInterface).getActualTypeArguments();

                for (Type genericType : genericTypes) {

                    System.out.println("Generic type: " + genericType);
                }
            }
    }
    }

    @Override
    public Iterator<Integer> iterator() {
        // TODO Auto-generated method stub
        return null;
    }

}

1 Comment

Now try that on the Type returned via reflection on a method returning Bar<String> as declared in my example above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.