0

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?

Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
Ali B
  • 1,047
  • 1
  • 13
  • 27
  • 1
    What do you intend to do with the list before returning it from your utility? – Sotirios Delimanolis Oct 02 '15 at 18:39
  • To answer the question, no, it's not possible. – Sotirios Delimanolis Oct 02 '15 at 18:40
  • `Class x = Class.forName(cType.getName());`: How is `x` different from `cType`? Are you trying to load different classes with the same name in two different class loaders? – Mike Samuel Oct 02 '15 at 18:47
  • Why would you want this? Expand your example to show how you'll use `list`. It's likely that your true goal is probably achievable, but you've jumped to conclusions about how to reach it. – erickson Oct 02 '15 at 18:55
  • The reason for doing this is: I have a number of classes that inherit from Parent, and more of these can be added as time goes by. In this method, I need to create a list for each subtype, as I get a master list of all the subtypes, and I need to partition it based on subtype. I'd love to hear alternate solutions. – Ali B Oct 02 '15 at 19:49

3 Answers3

1

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.

Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
  • 1
    In addition to this answer, this post on [type erasure](http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens) should help. – Kevin Meredith Oct 02 '15 at 18:47
1

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;
  } 
}
Mike Samuel
  • 109,453
  • 27
  • 204
  • 234
0

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.

Sean Mickey
  • 7,318
  • 2
  • 27
  • 56