Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double wildcard parameterization (nested wildcards) with Java generics

Tags:

java

generics

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.

like image 207
Pavel S. Avatar asked Feb 14 '14 12:02

Pavel S.


3 Answers

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:

  • Java Generic List<List<? extends Number>>
  • Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
  • What is PECS (Producer Extends Consumer Super)?

Similar posts:

  • Java: Wildcard Types Mismatch Results in Compilation Error
  • Issue with declaration of Map<String,Class<? extends Serializable>>
  • Bounded-wildcard related compiler error
like image 72
Paul Bellora Avatar answered Nov 15 '22 12:11

Paul Bellora


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.

like image 35
Pablo Lozano Avatar answered Nov 15 '22 13:11

Pablo Lozano


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.

like image 1
Smutje Avatar answered Nov 15 '22 14:11

Smutje