I have;
List stringList = new ArrayList();
List integerList = new ArrayList();
Is
You can do the same for method parameters as well:
Method method = someClass.getDeclaredMethod("someMethod");
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
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).
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.
The generic type of a collection should only matter if it actually has objects in it, right? So isn't it easier to just do:
Collection<?> myCollection = getUnknownCollectionFromSomewhere();
Class genericClass = null;
Iterator it = myCollection.iterator();
if (it.hasNext()){
genericClass = it.next().getClass();
}
if (genericClass != null) { //do whatever we needed to know the type for
There's no such thing as a generic type in runtime, but the objects inside at runtime are guaranteed to be the same type as the declared generic, so it's easy enough just to test the item's class before we process it.
Another thing you can do is simply process the list to get members that are the right type, ignoring others (or processing them differently).
Map<Class<?>, List<Object>> classObjectMap = myCollection.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Object::getClass));
// Process the list of the correct class, and/or handle objects of incorrect
// class (throw exceptions, etc). You may need to group subclasses by
// filtering the keys. For instance:
List<Number> numbers = classObjectMap.entrySet().stream()
.filter(e->Number.class.isAssignableFrom(e.getKey()))
.flatMap(e->e.getValue().stream())
.map(Number.class::cast)
.collect(Collectors.toList());
This will give you a list of all items whose classes were subclasses of Number
which you can then process as you need. The rest of the items were filtered out into other lists. Because they're in the map, you can process them as desired, or ignore them.
If you want to ignore items of other classes altogether, it becomes much simpler:
List<Number> numbers = myCollection.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.collect(Collectors.toList());
You can even create a utility method to insure that a list contains ONLY those items matching a specific class:
public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
return input.stream()
.filter(cls::isInstance)
.map(cls::cast)
.collect(Collectors.toList());
}
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
).
Use Reflection to get the Field
for these then you can just do: field.genericType
to get the type that contains the information about generic as well.