lundi 31 juillet 2017

Java reflection: getting correct type of parameterized generic superclass method

I am using reflection to discover methods of classes and their superclasses, along with the types of method arguments and return values. This mostly works, but I'm having trouble with some specific generic cases. Suppose I have a base class:

package net.redpoint.scratch;
public class Base<E> {
    public E getE() { return null; }
}

And a subclass:

package net.redpoint.scratch;
public class Derived extends Base<String> {}

Using reflection I can walk through the methods of Derived and get arg and return types (code omitted, but it works fine). However, I also want to know the inherited methods. Using code below, I can come very, very close. But getting the correct return type of getE() eludes me. I can get the generic type "E" but not the actual type "java.lang.String":

package net.redpoint.scratch;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class Scratch {
  public static void main(String[] args) throws Exception {
    Class clazz = Class.forName("net.redpoint.scratch.Derived");
    Type superType = clazz.getGenericSuperclass();
    if (superType instanceof ParameterizedType) {
      ParameterizedType superPt = (ParameterizedType)superType;
      Type[] typeArgs = superPt.getActualTypeArguments();
      Type t0 = typeArgs[0];
      // This is "java.lang.String"
      System.out.println(t0.getTypeName());
      Type superRawType = superPt.getRawType();
      if (superRawType instanceof Class) {
        Class superRawClazz = (Class)superRawType;
        for (Method method : superRawClazz.getDeclaredMethods()) {
          if (method.getName().equals("getE")) {
            Type returnType = method.getGenericReturnType();
            if (returnType instanceof TypeVariable) {
              TypeVariable tv = (TypeVariable)returnType;
              // This is "E"
              System.out.println(tv.getName());
              // How do I associate this "E" back to the correct type "java.lang.String"
            }
          }
        }
      }
    }
  }
}

The output is:

java.lang.String
E

My question is, how do I find that "E" is actually "java.lang.String"? I suspect it has something to do with a lookup into the typeArgs[], but I don't see how to get there. PLEASE do not respond unless you've actually worked with generics reflection. I've seen a lot of posts with answers like "type erasure prevents this", which is not true.





Aucun commentaire:

Enregistrer un commentaire