15

I'm looking to access the generic type of a declared field during runtime. I was previously under the impression that this was not possible due to the Java type erasure. However, this must not be the case because some well known frameworks leverage the generic type through reflection during runtime.

As an example, Guice will implement a Provider based upon the generic type you provide:

public class Injectable{

    @Inject
    private Provider<SomeType> someTypeProvider;

}

How does one access the 'SomeType' generic attribute of a field or any such type/method/etc through the reflection API?

Additionally it would be helpful to also know how to access these generic type attributes through the Java 6 Annotation Processor API.

Thanks.

Edit:

Thank you all for your great pointers. I found a way to do this using haylem's links, specifically the one to Prenkov's article Java Reflection: Generics.

Here's the answer I was looking for:

/**
 * @author John Ericksen
 */
public class TypeReflectionExample {

    public class SomeType{}

    public class Injectable{
        @Inject  private Provider<SomeType> someTypeProvider;
    }

    public static void main(String[] args){

        try {
            Field providerField = Injectable.class.getDeclaredField("someTypeProvider");

            Type genericFieldType = providerField.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);
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }   
}

results in:

fieldArgClass = class test.TypeReflectionExample$SomeType

The same can be done for Methods, Constructors, Superclass extensions/implements, etc

I'm awarding haylem, as his post led me to this solution, even if it didn't directly answer my question.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
John Ericksen
  • 10,995
  • 4
  • 45
  • 75

4 Answers4

15

It is true that generics aren't generally known at runtime in Java, because they are implemented with Type Erasure.

Reflecting Generics?

However, you can stil extract some valuable information about the declared types (NOT the runtime objects' types), as presented in Ian Roberston's article Reflecting Generics and Prenkov's article Java Reflection: Generics.

Background on Generics and Type Erasure

Generics where introduced while conserving backwards compatibility at the source qnd binary level, hence some of their limitation, like:

  • the impossibility to have a short-hand form without at least some indicator for generics support (here, the so-called diamond operator <>),
  • the impossibility to inspect generic-types at runtime, because they had to be implemented with Type Erasure.

Further Reading

Community
  • 1
  • 1
haylem
  • 22,460
  • 3
  • 67
  • 96
  • 1
    Ok, then how does Guice figure out what Provider is appropriate, as in the example I gave? – John Ericksen Mar 03 '12 at 18:49
  • Basically, if you specified a concrete type parameter in the code, then Guice can see the parameter that method was *declared* with via reflection. – Louis Wasserman Mar 03 '12 at 18:55
  • @Anony-Mousse: Sorry, I was still typing this while you were adding your comment. I point to Robertson's article using these, indeed. (I usually write answers in small bursts to prevent typing for nothing in questions get closed, or I accidentally close a Window, and because I think it can avoid multiple people typing in very similar answers if they see that one is already on the way). Thanks for pointing it out though. – haylem Mar 03 '12 at 19:00
  • @LouisWasserman: Exactly. You don't have access to the runtime object's actual type, but you have access to what was actually declared for it. – haylem Mar 03 '12 at 19:01
  • 1
    How is this answer relevant to the question? The OP didn't ask for links to java tutorials – Varun Achar Mar 03 '12 at 19:01
  • @VarunAchar: All the pages I linked to give backgound on the Generics, their design and implementation and reasons for not allowing the access to runtime objects' type, as opposed to other languages that didn't need to maintain compability and were able to do it "properly". – haylem Mar 03 '12 at 19:03
  • I'm with @VarunAchar. A good answer to this question would include at least some sample code to do (something analogous to) what Guice does. (That said, it's not downvote-worthy to me.) – millimoose Mar 03 '12 at 19:16
  • No, it's up-vote worthy to me as it contains much useful information. – Hovercraft Full Of Eels Mar 03 '12 at 19:59
  • @HovercraftFullOfEels: Thank you. They have a point though: a quick code sample is clearer than a lot of docs in some cases. However the linked articles hold copyright and I'm gone for the week-end, so I don't want to post anything with errors as long as I cannot test it. If that's not good enough for some, it's fine with me. – haylem Mar 03 '12 at 20:28
4

Well, why don't you look at what guice does? The source is publicly available.

Guice does these things at multiple levels. One that particular sticks out are type literals.

The key point here is that while types are compiled using type erasure (so there is only one class for each type), there still exist multiple Type objects, that do know the generics used. However, the code is optimized independently of that (as it was compiled per class, and not per type).

Have a look at the Java API of ParameterizedType.

So while it is correct that Java Generics are implemented by "type erasure" on a class level, this doesn't completely hold on a Type level. Unfortunately, handling Types is much more tricky than classes. Plus, this also implies that Java cannot optimize generics in the same way that C++ does, in particular for primitive types. An ArrayList<Integer> will by design be an ArrayList<?> containing objects, and not backed by a native int[] array when possible.

Note that this is, however, rather close to keeping track of these things yourself. Say, very naively (it will not work with nested generics), you could just extend ArrayList<T> with a class, that has a field Class<T> contentClass, then you will be able to find out this information at runtime. (A TypeLiteral may be the better choice instead of a Class here, though!) Plus, the JRE will actually not ensure that the list remains consistent. Just like you could cast a ArrayList<Integer> into an untyped ArrayList and add a String object.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
  • I was going to check out the Guice source next, just thought I would ask the knowledgeable people here first. Thanks for the pointers. – John Ericksen Mar 03 '12 at 19:00
  • 3
    Don't compare generics and templates, it's misleading to imply the two language features have anything significant in common. (They definitely don't have anything in common w/r/t the mental model needed to understand them.) – millimoose Mar 03 '12 at 19:21
  • They may work completely different (and Templates can serve various other purposes). But they often serve very similar needs, in particular for type specialization. +1 for that reminder though. – Has QUIT--Anony-Mousse Mar 03 '12 at 21:08
2

I've used this a year or so ago. It could help you out

Type typeOfSrc = type(YourClass.class, clazz);

// type(C, A1,...,An) => C<A1,...,An>
static ParameterizedType type(final Class raw, final Type... args)
{
    return new ParameterizedType()
    {
        public Type getRawType(){ return raw; }

        public Type[] getActualTypeArguments(){ return args; }

        public Type getOwnerType(){ return null; }
    };
}

This allows you to access the Generic type at run time. Basically what you're doing here is storing the Generic information in another class, and using that class for retrieving that information at run time.

Varun Achar
  • 14,781
  • 7
  • 57
  • 74
0

I believe guice uses TypeLiteral to encapsulate generic information in a separate object.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
vladimir e.
  • 723
  • 3
  • 7