5

I'd like to get the generic type of a collection, using reflection, at runtime.

Code (JAVA):

Field collectionObject = object.getClass().getDeclaredField(
    collectionField.getName());
//here I compare to see if a collection
if (Collection.class.isAssignableFrom(collectionObject.getType())) {
   // here I have to use the generic type of the collection 
   // to see if it's from a specific type - in this case Persistable
   if (Persistable.class.isAssignableFrom(GENERIC_TYPE_COLLECTION.class)) {
   }
}

Is there a way of getting the generic type of the collection in java at runtime? In my case I need the .class of the collection's generic type.

Thanks in advance!

akarnokd
  • 69,132
  • 14
  • 157
  • 192
gege
  • 371
  • 1
  • 5
  • 13
  • I was facing similar problem and this is what finally solved it for me: http://stackoverflow.com/a/183232/639119 – Zds Oct 04 '12 at 18:43

4 Answers4

17

Type erasure means that information about the generic type of an object simply isn't present at execution time.

(The link is to the relevant section of Angelika Langer's Java Generics FAQ which should answer virtually every question you could possibly ask about Java generics :)

However, you're not really interested in the type of an object - you're interested in the type of a field. I misread the question, and although the answer has been accepted I hope to make amends by fixing it now :)

If the field doesn't use a type parameter itself, it can be done. For example:

import java.lang.reflect.*;
import java.util.*;

public class Test
{
    public List<String> names;

    public static void main(String [] args)
        throws Exception // Just for simplicity!
    {
        Field field = Test.class.getDeclaredField("names");

        ParameterizedType type = (ParameterizedType) field.getGenericType();

        // List
        System.out.println(type.getRawType());

        // Just String in this case
        for (Type typeArgument : type.getActualTypeArguments())
        {
            System.out.println("  " + typeArgument);
        }
    }
}

If the field were in a class T with the field being List<T> then you'd have to know the type argument for the instance in order to know the type argument for the collection.

Translating this into your required code is somewhat tricky though - you really need to know the type argument at the point of the collection class. For instance, if someone declared:

public class StringCollection implements Collection<String>

and then had a field of type StringCollection, that field itself wouldn't have any type arguments. You'd then need to check getGenericSuperType and getGenericInterfaces recursively until you found what you wanted.

It's really not going to be easy to do that, even though it's possible. If I were you I'd try to change your design so that you don't need this.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    The recursive checking indeed is not easy, there are many corner cases to take into account. And that's just the first step, next you need an isAssignableFrom equivalent that handles generics too. I wouldn't recommend trying to implement it yourself. I wrote a library that does this for you, called gentyref. See http://code.google.com/p/gentyref/ . – Wouter Coekaerts Jun 08 '10 at 21:28
9

You can absolutely do what you want. Type erasure means you can't inspect an instance of a collection for its generic type, but you can certainly inspect a field for its generic type.

class Thing {
  List<Persistable> foo;
}


Field f = Thing.class.getDeclaredField("foo");
if( Collection.class.isAssignableFrom( f.getType() ) {
   Type t = f.getGenericType();
   if( t instanceof ParameterizedType ) {
     Class genericType = (Class)((ParameterizedType)t).getActualTypeArguments()[0];
     if( Persistable.class.isAssignableFrom( genericType ) )
         return true;
   }
}

There's a lot of things which can go wrong here, for example, if you have

Class Thing<T> {
  List<T> foo;
}

then the above won't work.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Darren Gilroy
  • 2,071
  • 11
  • 6
  • 1
    I'm guessing this line: Class genericType = (Class)((ParameterizedType)t).getActualTypeArguments[0]; should be Class genericType = (Class)((ParameterizedType)t).getActualTypeArguments()[0]; ? – andersonbd1 Dec 15 '09 at 15:55
0

You can get the generic type of the field you read the object from with Field.getGenericType. Note that the field may be a raw type, be a rare type (generic but with a generic argument that is raw), use the type of the instance, use wildcards, etc.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

Copied from my post at: https://stackoverflow.com/questions/1004022/java-generic-class-determine-type/1005283#1005283

I've used a similar solution to what he explains here for a few projects and found it pretty useful.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

The jist of it is using the following to determine the type parameter at runtime:

public Class returnedClass {
  ParameterizedType parameterizedType =
    (ParameterizedType) getClass().getGenericSuperClass();
 return (Class) parameterizedtype.getActualTypeArguments()[0];
}
Community
  • 1
  • 1
jnt30
  • 1,367
  • 2
  • 15
  • 21