Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics: Accessing Generic Type at runtime

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.

like image 841
John Ericksen Avatar asked Mar 03 '12 18:03

John Ericksen


People also ask

Is generic type information present at runtime?

Generic type information is not present at runtime.

How do you find the type of generic class?

You can get around the superfluous reference by providing a generic static factory method. Something like public static <T> GenericClass<T> of(Class<T> type) {...} and then call it as such: GenericClass<String> var = GenericClass. of(String. class) .

Is Java generics compile time or runtime?

Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure. For example, List<Integer> will be converted to the non-generic type List , which ordinarily contains arbitrary objects.

Can generics take multiple type parameters?

Multiple parametersYou can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.


2 Answers

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

  • From The Java Tutorial:
    • section on Generic Types
    • section on Type Inference and Instantiation of Generic Classes
  • From the Java Language Specifications (JLS):
    • Java SE 5's JLS section on Types, Values and Variables
    • Java SE 7's JLS section on Types, Values and Variables
  • From good StackOverflow questions:
    • Java Raw Type and generics interaction
  • Others:
    • IBM Developer Series: Java Theory and Practice: Generics Gotchas (especially the sections The Road Not Taken, Generifying Existing Classes and Implications of Erasure).
like image 147
haylem Avatar answered Sep 19 '22 05:09

haylem


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.

like image 21
Has QUIT--Anony-Mousse Avatar answered Sep 22 '22 05:09

Has QUIT--Anony-Mousse