Wednesday, April 25, 2012

Get generic type of java.util.List


I have;




List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();



Is there a (easy) way to retrieve the generic type of the list?


Source: Tips4all

8 comments:

  1. If those are actually fields of a certian class, then you can get them with a little help of reflection:

    package test;

    import java.lang.reflect.Field;
    import java.lang.reflect.ParameterizedType;
    import java.util.ArrayList;
    import java.util.List;

    public class Test {

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception {
    Field stringListField = Test.class.getDeclaredField("stringList");
    ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
    Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
    System.out.println(stringListClass); // class java.lang.String.

    Field integerListField = Test.class.getDeclaredField("integerList");
    ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
    Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
    System.out.println(integerListClass); // class java.lang.Integer.
    }
    }


    You can also do that for parameter types and return type of methods.

    But if they're inside the same scope of the class/method where you need to know about them, then there's no point of knowing them, because you already have declared them yourself.

    ReplyDelete
  2. Short answer: no.

    This is probably a duplicate, can't find an appropriate one right now.

    Java uses something called type erasure, which means at runtime both objects are equivalent. The compiler knows the lists contain integers or strings, and as such can maintain a type safe environment. This information is lost (on an object instance basis) at runtime, and the list only contain 'Objects'.

    You CAN find out a little about the class, what types it might be parametrized by, but normally this is just anything that extends "Object", i.e. anything. If you define a type as

    class <A extends MyClass> AClass {....}


    AClass.class will only contain the fact that the parameter A is bounded by MyClass, but more than that, there's no way to tell.

    ReplyDelete
  3. The type is erased so you will not be able to. See http://en.wikipedia.org/wiki/Type%5Ferasure and http://en.wikipedia.org/wiki/Generics%5Fin%5FJava#Type%5Ferasure

    ReplyDelete
  4. As others have said, the only correct answer is no, the type has been erased.

    If the list has a non-zero number of elements, you could investigate the type of the first element ( using it's getClass method, for instance ). That won't tell you the generic type of the list, but it would be reasonable to assume that the generic type was some superclass of the types in the list.

    I wouldn't advocate the approach, but in a bind it might be useful.

    ReplyDelete
  5. Generally impossible, because List<String> and List<Integer> share the same runtime class.

    You might be able to reflect on the declared type of the field holding the list, though (if the declared type does not itself refer to a type parameter whose value you don't know).

    ReplyDelete
  6. At runtime, no, you can't.

    However via reflection the type parameters are accessible. Try

    for(Field field : this.getDeclaredFields()) {
    System.out.println(field.getGenericType())
    }


    The method getGenericType() returns a Type object. In this case, it will be an instance of ParametrizedType, which in turn has methods getRawType() (which will contain List.class, in this case) and getActualTypeArguments(), which will return an array (in this case, of length one, containing either String.class or Integer.class).

    ReplyDelete
  7. You can do the same for method parameters as well:

    Type[] types = method.getGenericParameterTypes();
    //Now assuming that the first parameter to the method is of type List<Integer>
    ParameterizedType pType = (ParameterizedType) types[0];
    Class<?> clazz = (Class<?>) pType.getActualTypeArguments()[0];
    System.out.println(clazz); //prints out java.lang.Integer

    ReplyDelete
  8. import org.junit.Assert;
    import org.junit.Test;

    import java.lang.reflect.Field;
    import java.lang.reflect.ParameterizedType;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;

    public class GenericTypeOfCollectionTest {
    public class FormBean {
    }

    public class MyClazz {
    private List<FormBean> list = new ArrayList<FormBean>();
    }

    @Test
    public void testName() throws Exception {
    Field[] fields = MyClazz.class.getFields();
    for (Field field : fields) {
    //1. Check if field is of Collection Type
    if (Collection.class.isAssignableFrom(field.getType())) {
    //2. Get Generic type of your field
    Class fieldGenericType = getFieldGenericType(field);
    //3. Compare with <FromBean>
    Assert.assertTrue("List<FormBean>",
    FormBean.class.isAssignableFrom(getFieldGenericTypefieldGenericType));
    }
    }
    }

    //Returns generic type of any field
    public Class getFieldGenericType(Field field) {
    if (ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())) {
    ParameterizedType genericType =
    (ParameterizedType) field.getGenericType();
    return ((Class)
    (genericType.getActualTypeArguments()[0])).getSuperclass();
    }
    //Returns dummy Boolean Class to compare with ValueObject & FormBean
    return new Boolean(false).getClass();
    }
    }

    ReplyDelete