0

I'm trying to use reflection to get a list of classes implementing an abstract class, Detector. I would like to use the elements of this list (of type Class<T extends Detector>) as a type parameter for another class, DetectorWrapper<T extends Detector>, so that I can easily instantiate T within that class (i.e., using new T(...)). Is it possible to use reflection for this, or should I instead pass Class<T> to DetectorWrapper and use reflection to invoke the constructor whenever I need an instance of T?

  • This question is not a duplicate of the linked question. I found those solutions in my search, but they do the exact **opposite** of what I want. In the linked question, the type `T` is retrieved. I do not want to query the type of a given reflected parameterized type, but rather I want to use a reflected type as a type parameter. I'm not sure how to improve the question to clarify this. – Rens van der Heijden Jun 01 '16 at 18:46

1 Answers1

1

There is no way to use a Class object as a type parameter, but that doesn’t matter, as a type parameter is exactly what you can never instantiate anyway, i.e. you can’t say new T().

So you need a Class object to instantiate an object of that type and may define a relationship to the type parameter:

class DetectorWrapper<T extends Detector> {
    final List<T> detectors;

    DetectorWrapper(Class<? extends T>... types) {
        ArrayList<T> result=new ArrayList<>(types.length);
        try {
            for(Class<? extends T> type: types) result.add(type.newInstance());
        }catch(InstantiationException|IllegalAccessException ex) {
            throw new IllegalArgumentException(ex);
        }
        this.detectors=result;
    }
}

Though for most practical issues, the type parameter of the DetectorWrapper class is obsolete, i.e. you may use

  class DetectorWrapper {
      final List<Detector> detectors;

      DetectorWrapper(Class<? extends Detector>... types) {
          ArrayList<Detector> result=new ArrayList<>(types.length);
          try {
              for(Class<? extends Detector> type: types) result.add(type.newInstance());
          }catch(InstantiationException|IllegalAccessException ex) {
              throw new IllegalArgumentException(ex);
          }
          this.detectors=result;
      }
  }

Having classes like

class Detector {}
class Foo extends Detector {}
class Bar extends Detector {}

you can instantiate a DetectorWrapper via new DetectorWrapper(Foo.class, Bar.class).

Note that Class.newInstance() assumes a no-arg constructor. You may use type.getConstructor(parameterTypes).newInstance(initargs); instead, in case you know a common constructor signature.

With Java 8, there is a more robust, non-reflective alternative:

class DetectorWrapper {
    final List<Detector> detectors;

    DetectorWrapper(Supplier<? extends Detector>... types) {
        ArrayList<Detector> result=new ArrayList<>(types.length);
        for(Supplier<? extends Detector> s: types) result.add(s.get());
        this.detectors=result;
    }
}

which can be instantiated like new DetectorWrapper(Foo::new, Bar::new), assuming the same setup as above.

An equivalent alternative implementation would be:

class DetectorWrapper {
    final List<Detector> detectors;

    DetectorWrapper(Supplier<? extends Detector>... types) {
        detectors=Arrays.stream(types).map(Supplier::get).collect(Collectors.toList());
    }
}

But if you discover the Class objects dynamically at runtime, there is no way around the reflective instantiation.

Holger
  • 243,335
  • 30
  • 362
  • 661
  • Oddly enough, `new T()` seemed to compile fine once I specified ``, but I can see now why it is a bad idea. I was also using `T` to call some static methods that I defined on `Detector`, which was my additional motivation for this use case. Thanks for the extensive answer, especially the non-reflective example! – Rens van der Heijden Jun 02 '16 at 14:30
  • 1
    `new T()` can’t work if `T` is referring to a type parameter. There’s possibly a different `T` in scope or another misunderstanding. Using `T.method` to invoke a `static` method of `Detector` has no benefit (if you think that `T` is less to type compared to `Detector`, consider `import static`. By the way, I forgot to add `@SafeVarargs` to the constructors, you really want to do that in production code… – Holger Jun 02 '16 at 14:40