5

Consider the following Java code:

public class Test
{
    private Foo< String, String > foo1;
    private Foo< Integer, Integer > foo2;
}

public class Foo< T, V >
{
    private Bar< V > bar;
    private T a;
}

public class Bar< T >
{
    @MyAnnotation
    private List< T > list;
}

First, starting with the class named 'Test', I'm searching all fields recursively, which are annotated with 'MyAnnotation' and whose type is derived from 'java.lang.Collection'. For the given example, the results would be:

  • Test.foo1.bar.list
  • Test.foo2.bar.list

It's obviosly clear, that the first one can only take strings for its elements, whereas the second one can only take integers.

My Question is: What is the best (easiest) way to find all Collection fields annotated with 'MyAnnotation' and tell their element type?

Finding annotated fields from type java.lang.Collection is not the problem. But how can we get a result which looks like the following one for the given example?

  • Test.foo1.bar.list< String >
  • Test.foo2.bar.list< Integer >

I've tried several approaches, which always gave me 'Object' as the collection's element type instead of 'String'.

I'm really stuck on this. Many thanks in advance!

theV0ID
  • 4,172
  • 9
  • 35
  • 56

2 Answers2

14

May be you can try like this:

Field field = Test1.class.getField("list");

Type genericFieldType = field.getGenericType();

if(genericFieldType instanceof ParameterizedType){
    ParameterizedType aType = (ParameterizedType) genericFieldType;
    Type[] fieldArgTypes = aType.getActualTypeArguments();
    for(Type fieldArgType : fieldArgTypes){
        Class fieldArgClass = (Class) fieldArgType;
        System.out.println("fieldArgClass = " + fieldArgClass);
    }
}

This will return fieldArgClass = java.lang.String

UVM
  • 9,776
  • 6
  • 41
  • 66
0

This is not possible due to Java's type erasure. When compiled, the generic types will be replaced by raw types, resulting in something like this:

public class Test
{
    private Foo foo1;
    private Foo foo2;
}

public class Foo
{
    private Bar bar;
    private Object a;
}

public class Bar
{
    @MyAnnotation
    private List list;
}

The actual types are then filled in places where you are directly working with foo1 and foo2. For example, the following code:

Test test = new Test();
List<String> a = test.foo1.bar;
String b = a.get(0);
Integer b = test.foo2.a;

will be translated to something like:

Test test = new Test();
// The generic type is erased here...
List a = test.foo1.bar;
// ...and a cast is added here
String b = (String) a.get(0);
Integer b = (Integer) test.foo2.a;

Long story short: there's no way you can retrieve that type, since it is discarded at compile time.

Mattias Buelens
  • 19,609
  • 4
  • 45
  • 51
  • http://stackoverflow.com/questions/2320658/why-are-not-all-type-information-erased-in-java-at-runtime – Matthias Wuttke Jul 31 '14 at 08:06
  • @MatthiasWuttke Not sure what you're trying to say here, you didn't really comment much. That question is about a "trick" to get generic type information, but it relies on *always* using an (anonymous) superclass to fill in the generic type and it doesn't work in all cases (e.g. `new ArrayList() {};` wouldn't work). Still I don't see how that's relevant to *this* question... – Mattias Buelens Jul 31 '14 at 23:26
  • I found both questions using Google, and the second one I linked helped me with my problem. (I needed to find out the generic type argument of a collection field.) While your answer certainly is right and helps understanding Java type erasure, the question I linked solved my problem. I added the comment to help others, but should have added more than the link. I didn't want to step on your toos, so please accept my apologies. – Matthias Wuttke Aug 01 '14 at 06:50
  • @MatthiasWuttke Okay, not a problem. Thanks for the explanation. :-) – Mattias Buelens Aug 01 '14 at 08:02