7

I am using a method from a third party library (Reflections) which is supposed to find subtypes of the given type and looks like

public <T> Set<Class<? extends T>> getSubTypesOf(final Class<T> type) {
...

When the caller code looks like

Class<?> type = ...
Set<Class<?>> subTypes = reflections.getSubTypesOf(type);

I am getting a compile error: "cannot convert from Set<Class<? extends capture#19-of ?>> to Set<Class<?>>". The following fixes the situation:

Class<?> type = ...
Set<?> subTypes = reflections.getSubTypesOf(ht);

so it looks like the only possible remedy for incorrect Set<Class<? extends ?>> would be Set<?> but not Set<Class<?>>. Why is it so? Thanks for any explanation on this.

Pavel S.
  • 1,202
  • 1
  • 13
  • 29

4 Answers4

10

Use the following instead:

Set<? extends Class<?>> subTypes = reflections.getSubTypesOf(type);

The problem is that nested wildcards don't perform type capture. Declaring a Set<Class<?>> means "a set of classes of any type", while what's being returned is a Set<Class<? extends capture#19-of ?>>, which means "a set of classes of any type extending from some specific unknown type". In this case, that "specific unknown type" is derived from the type argument to T, which is inferred from type to be an unbounded wildcard capture (the ? in Class<?>).

For example, pretend that "specific unknown type" is Number:

Class<Number> type = ...
Set<Class<?>> subTypes = reflections.getSubTypesOf(type);

Here, getSubTypesOf returns a Set<Class<? extends Number>>. That type is not assignable to Set<Class<?>> because generic types aren't covariant. But, wildcard capture helps us express covariance, allowing types like Set<? extends Class<?>>. The caveat is that we can't add anything but null to such a set, since we don't know its specific type.

Related posts:

Similar posts:

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • This really works, thank you! Frankly speaking I don't understand for now where `? extends Class>` comes from, as it makes me think that we start to deal with extending `Class` which is not what my code is about at all. Ok, will study your overall list of references in more detail :) – Pavel S. Feb 14 '14 at 15:06
  • 1
    @PavelS Glad to help. When used to express generic bounds, `extends` doesn't necessarily imply a subclass, just a *subtype*. For example `Class extends Number>` is a subtype of `Class>`. Also note that in this context `Class>` would be considered a subtype of itself, so `Set>` would be assignable to `Set extends Class>>`. – Paul Bellora Feb 14 '14 at 19:29
2

You must consider '?' means 'Unknown', not 'any'.

Set<Class<?>> subTypes = reflections.getSubTypesOf(type);

subTypes is a set of Class 'unknown'. It could be String, Integer, List... any of them.

getSubTypesOf(type) will return a certain but unknown set of classes. I use 'certain' because we know all will extend the class of type. So it's not the same. The problem is it seems you don't know the class of type on compilation time.

Set<Class> should work too, but it will show a warning because you don't use the generic with Class.

Pablo Lozano
  • 10,122
  • 2
  • 38
  • 59
  • Now I got it (at least partially :)). Looks like by saying _"'?' means 'Unknown', not 'any'"_, you exactly describe what's going on. Even if I think of `Class> type` parameter as of _any class_, the result of call to `getSubTypesOf` is not "any classes", but ones constrained by being subtypes of `type`, so it cannot look like `Class>` just in the same way as `type`. I wish however there were some pretty looking way to handle such a situation. – Pavel S. Feb 14 '14 at 13:55
1

As ? is the wildcard, when you insert a Class<?>, the method returns a Set<Class<? extends ?>> (purely theoretical and not supported by Java) - try calling the method with a concrete type (e.g. Class<Number>) and you should get a Set<Class<? extends Number>> returned.

Smutje
  • 17,733
  • 4
  • 24
  • 41
  • Unfortunately I can't know the concrete class of `type` parameter because it is read before from an annotation on another class, and is allowed to be of any type. – Pavel S. Feb 14 '14 at 13:19
  • Then you have to add a type parameter to the method which calls the reflection call, that you have a `Class` to call the reflection method with. – Smutje Feb 14 '14 at 13:41
1

The following will help: Your have ? extends T (restricted by T) for Class in right side (getSubTypesOf returns it), so you need to extend it in left side too:

Class<? extends T> type;
Set<Class<? extends T>> subTypesOf = getSubTypesOf(type);

instead of:

Set<Class<?>> subTypes = reflections.getSubTypesOf(type);
Ashot Karakhanyan
  • 2,804
  • 3
  • 23
  • 28
  • 1
    That would mean he has to extend his own method by a type parameter - you otherwise can not write `Class extends T>` as a local variable type. – Smutje Feb 14 '14 at 13:02
  • That means that in left side u can receive only `Class` or `Class`. T is for restriction from up. Method returns `Set>` so your left side should be restricted by T too. – Ashot Karakhanyan Feb 14 '14 at 13:06
  • @Smutje `T` would be an actual class, for example `Set> subTypesOf = getSubTypesOf(Number.class);` – Bohemian Feb 14 '14 at 13:19
  • @AshotKarakhanyan I see your point, you mean `? extends ? extends T` will reduce to `? extends T` this way. However this is the final code which is not to be parameterized by something more concrete than `?`. I am totally agnostic of what `T` can be, because it can be anything. – Pavel S. Feb 14 '14 at 13:29
  • @PavelS, I don't understand what do you mean with `? extends ? extends T`, but when don't inherit from T you get type mismatch, and compiler says about it. Also take a look for more detailed descriptions, so if right size (the method) just use with `Set>` where is the reason to not use? http://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html. – Ashot Karakhanyan Feb 14 '14 at 13:41
  • @AshotKarakhanyan BTW, the solution you provide in your answer produces a compile error as well (I use a concrete type T for the example): `Type mismatch: cannot convert from Set> to Set>`. I guess this is because the result of `getSubtypesOf` produces classes that are subtypes of `type` (which is subtype of T), but not _any_ classes which are subtypes of T. – Pavel S. Feb 14 '14 at 14:17
  • How?? For example just change `T` by `Integer` and it works normal `static public Set> getSubTypesOf(final Class type) {}` and adding `Class type = null; final Set> subTypesOf = getSubTypesOf(type);` works normal. It seams you did something not iususal. – Ashot Karakhanyan Feb 14 '14 at 14:23
  • Yes, it compiles successfully this way as you write. I just mean that in your original answer `type` is declared as `Class extends T>`, as well as result of `getSubTypesOf`. So it would be `Class extends Integer> type=null;` in your last example which does not compile. – Pavel S. Feb 14 '14 at 14:53
  • Yes, make changing in my provided example is assume, that you need changes also in `getSubTypesOf`return type and parameter also. If you made different change, please provide all code to compare and anderstand which is the problem of comparing. – Ashot Karakhanyan Feb 14 '14 at 15:02
  • @AshotKarakhanyan, this is my code of your example (sorry if I misunderstood what you meant): `public static Set> getSubTypesOf(final Class type) {return null;}`. The caller: `Class extends Integer> type = null; final Set> subTypesOf = getSubTypesOf(type);`. – Pavel S. Feb 14 '14 at 15:14
  • @PavelS Now I see: You can the method to `getSubTypesOf(final Class extends Integer> type)` OR declare `type` as `Class`. In both cases `type` left and right sides should be the same. Yes my last provided example, 55 min ago, has this problem. Before described changes fix it. – Ashot Karakhanyan Feb 14 '14 at 15:34
  • 1
    @AshotKarakhanyan OK, anyway I cannot change the `getSubTypesOf` method's signature, as it comes from a third party library. So making the left side in consistence with its return value was the original problem. See *Paul Bellora*'s answer where he shows how to form the left side type which will compile OK (however the logic is still vague to me). And thank you for you participation and interest! – Pavel S. Feb 14 '14 at 16:00