2

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()));
    }

  }
}
Jan Galinski
  • 11,768
  • 8
  • 54
  • 77
  • It appears to be type String, because that's what you extended. – duffymo Jul 23 '13 at 19:56
  • possible duplicate of [Get generic type of java.util.List](http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list) – Cᴏʀʏ Jul 23 '13 at 19:58
  • No, not a duplicate ... I have "List" where T is specified by the implementing class. Added Sample code what I am trying to do. – Jan Galinski Jul 23 '13 at 20:01
  • Where is that code sample? In the `Child` class? Can you add a `List getValues()` method that in `Child` should give you a `List`? Does that help? – Gray Jul 23 '13 at 20:21
  • Sorry, it seems this is a bit hard to described. I added a few lines on top. I am writing a code generator for existing classes. I picked a random example (Parent, Child), to describe my concrete problem: when I get the genericParameterTypes of Child#setValues(), I get "List", not "List". But I need List for my generated method. – Jan Galinski Jul 23 '13 at 20:47
  • What does your `getTypeVariable()` method look like? Where are you getting `type` from? – John Ericksen May 29 '14 at 14:27
  • Hi John, I edited the code sample. getTypeVariable() basically delegated to "codeModel.parseType(typeVariable.getName()" and handled the exception. Would be great if you have an idea to solve this ... – Jan Galinski May 30 '14 at 07:46

2 Answers2

1

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

Class<?> t = TypeResolver.resolveRawArgument(Parent.class, Child.class);
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Jonathan
  • 5,027
  • 39
  • 48
0

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
}
Java42
  • 7,628
  • 1
  • 32
  • 50