1

I have an object of type Class<?> and I'm trying to call a method with the signature:

<T extends A & B> void foo(Class<T> clazz);

How can I declare the argument to satisfy the signature?


Example:

void bar(String type) throws Exception {

    Class<?> clazz = Class.forName(type);

    if(!clazz.isAssignableFrom(A.class)) throw new IllegalArgumentException("type="+type);
    if(!clazz.isAssignableFrom(B.class)) throw new IllegalArgumentException("type="+type);

    //We know clazz implements both A and B, how do we call foo?
    foo(clazz.asSubclass(???)); 
}

<T extends A & B> void foo(Class<T> clazz) {
    //Success
}

Edit: I resorted to refactor the foo method into <T extends A> ... and moved the .isAssignableFrom(B.class) inside it.

Alexandru Severin
  • 6,021
  • 11
  • 48
  • 71

1 Answers1

1

Edit: Let me preprend my answer with this (see below for more info): reflection and generics aren't a good fit as they operate at different times:

  • generics primarily are a compile time tool to help the compiler spot potential programming errors such as trying to add strings to a list of numbers
  • reflection primarily is a runtime tool to get information about your classes/objects at runtime (such as resolveClass()) and due to type erasure most generics information is lost at runtime

Original answer:

T extends A & B only works if B is an interface (A can be a class or an interface) and the parameter must match both, i.e. it must be a subclass of A and implement B (assuming A doesn't already implement B in which case the A & B part would be unnecessary).

As an example let's use Number and Comparable:

<T extends Number & Comparable<T>> void foo(Class<T> clazz);

We could now call foo(Long.class) because Long extends Number implements Comparable<Long>.

We couldn't call foo(AtomicInteger.class) because AtomicInteger extends Number but it doesn't implement Comparable<AtomicInteger>.

We also couldn't call foo(Number.class) because Number matches extends Number but doesn't implement Comparable<Number>.

In your case you'd need to define a variable Class<? extends A & B> but that is not supported so you'd need to use a generic type (defined on the calling method or the class) or a concrete type:

<T extends A & B> bar() {
  Class<T> clazz = resolveClass(); //This would need the same generic type
  foo( clazz );
}

or

class C extends A implements B {}

and

Class<C> clazz = resolveClass(); //This would need the same generic type
foo( clazz );

As you can imagine that can become quite complex and might not be feasible. As a last ditch effort you could try to use raw types:

Class clazz = resolveClass(); //This would need the same generic type
foo( clazz );

This would produce a lot of warnings because it disables type checks so make sure your parameters are thoroughly checked. So if you need to do that it might hint at a design flaw so I'd suggest you think really hard about whether you need T extends A & B at all and what your code is doing.

Thomas
  • 87,414
  • 12
  • 119
  • 157