Reputation: 1117
So let's say I use reflection to find a class'subtypes (I'm using the Google Reflections library).
Set<Class<? extends Parent>> subTypes = reflections.getSubTypesOf(Parent.class);
now I want to create a list for each subtype, by iterating over the set's members, so I want something like this:
try {
for (Class<? extends Parent> cType : subTypes.iterator()) {
Class x = Class.forName(cType.getName());
List<x> list = new ArrayList<x>();
}
} catch(ClassNotFoundException ex){
...
}
However, this doesn't work, and the compiler complains that 'x is an unknown class' on the list definition.
Is there a way to define a list based on the reflected types?
Upvotes: 0
Views: 72
Reputation: 7716
If you already have this part working:
Set<Class<? extends Parent>> subTypes =
reflections.getSubTypesOf(Parent.class);
I think if you make the changes shown here, you will resolve your problem:
public <P extends Parent> void handleSubTypes(Set<Class<P>> subTypes) {
try {
for (Class<P> sType : subTypes) {
P instance = sType.newInstance();
List<P> list = new ArrayList<P>();
}
} catch (InstantiationException e) {
// ...
} catch (IllegalAccessException e) {
// ...
}
}
And here is a cut from the Class.newInstance()
JavaDoc:
Creates a new instance of the class represented by this Class object. The class is instantiated as if by a new expression with an empty argument list. The class is initialized if it has not already been initialized. Note that this method propagates any exception thrown by the nullary constructor, including a checked exception. Use of this method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler. The Constructor.newInstance method avoids this problem by wrapping any exception thrown by the constructor in a (checked) InvocationTargetException.
Upvotes: 0
Reputation: 120526
You could define
<T> static List<T> listForType(Class<T> elementType) {
return new ArrayList<T>();
}
and then you might try
Class<X> xClass = cType;
List<X> listOfX = listForType(xClass);
but there's no way for the language to manufacture an invariant type parameter X
from a variant type parameter ? extends Parent
in a type-safe manner.
If you do have an invariant type elsewhere, you might be able to get type-safe code by limiting the unchecked code to a small place as long as your sub-types are not themselves parameterized types.
final class ListPerSubType {
private final Map<Class<? extends Parent>, List> listPerSubType = new LinkedHashMap<>();
public <T extends Parent> List<T> get(Class<T> cl) {
// TODO: throw IllegalArgument if cl is parameterized.
if (!listPerSubType.containsKey(cl)) {
listPerSubType.put(cl, new ArrayList<T>());
}
List list = listPerSubType.get(cl);
// This is type-safe since no other code exposes the
// lists via type not gained from a type token.
@SuppressWarnings("unchecked")
List<T> typedList = list;
return list;
}
}
Upvotes: 1
Reputation: 726849
This is not possible to use a reflected type as a type parameter in Java. This is also of no use, because Java generics are useful only at compile time; once the compiler has finished, generic class instances on different type parameters become identical due to type erasure.
This means that the code which uses a reflected type for a generic parameter would not be any more useful than a code with generic parameter substituted for the parent interface (i.e. List<Parent>
in your case) or List<Object>
when there is no common base class / interface.
Upvotes: 1