How to get generic type "T" of class when reused in method

905 views Asked by At

I am building a code generator for Fluent API. I want to create a new class for every existing (POJO)-class. I dont have the existing classes under my control. I parse the existing methods via reflection and if I encounter a setter or an "add" method, I create a method for this in my Fluent-Wrapper class, so I can say child().values(....).get().

This is working fine with simple parameters. It took me a while to figure out how to deal with ParameterizedTypes, but I can manage this now. What I dont get is how to create a concrete method for a generic parameter which type is not defined on the method itself but on the containing class.

I have something like this:

abstract class Parent<T> {
   void setValues(List<T> values) {...};
}

class Child extends Parent<String> {}

Now I try to generate code via JCodeModel and need to parse the type of the parameter "values". Problem: I get a ParameterizedType that contains List and a TypeVariable "T". But I cannot map "T" back to String. But I cannot figure out how to get the concrete Type "T" got by implementing "Child". Anyone?

Clarify: I do this, and need not "T" but "String" when it comes to the case "TypeVariable"

JType result = codeModel._ref(typeToClass.apply(type));
if (isParameterizedType(type)) {
  for (Type typeArgument : ((ParameterizedType) type).getActualTypeArguments()) {
    if (typeArgument instanceof WildcardType) {
      result = narrow(result, codeModel.wildcard());
    } else if (typeArgument instanceof Class) {
      result = narrow(result, typeToClass.asJType(codeModel, typeArgument));
    } else if (typeArgument instanceof TypeVariable<?>) {
      TypeVariable<?> typeVariable = (TypeVariable<?>) typeArgument;
      // this is where I only get "T" but need "String"
      result = ((JClass) result).narrow(getTypeVariable(codeModel.parseType(typeVariable.getName()));
    }

  }
}
2

There are 2 answers

0
Jonathan On

If you are using my TypeTools library, this becomes pretty simple:

Class<?> t = TypeResolver.resolveRawArgument(Parent.class, Child.class);
0
Java42 On

Try this...tweak as necessary, add error checking etc...

private Class<T> getType(int index) {
    final ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
    return (Class<T>) parameterizedType.getActualTypeArguments()[index];
}

Usage:

Foo42<JFrame,String> foo = new Foo42<>();

if (getType(0).isAssignableFrom(JFrame.class)) {
    // is a JFrame
}

if (getType(1).isAssignableFrom(String.class)) {
    // is a String
}