0

I have following two classes:

class ProblematicConverter implements Converter<List<?>> {};
class NonProblematicConverter implements Converter<List> {};

And method of class Utils:

<T> void addConverter(Class<? extends T> cls, Converter<T> converter);

Now, first function invocation is ok but second produces error:

addConverter(List.class, new ProblematicConverter());
addConverter(List.class, new NonProblematicConverter());

Error says:

"The method addConverter(Class, Converter) in the type Utils is not applicable for the arguments (Class, ProblematicConverter)"

I do not understand why it's like that.

Trismegistos
  • 3,821
  • 2
  • 24
  • 41
  • `List` isn't a subtype of `List>`. (It's actually the other way.) What are you trying to do here? It seems unlikely to me that you'll get this working as-is. You need to use something like Guava `TypeToken` or else really loosen up the declaration of `addConverter`. – Radiodef Jun 07 '17 at 14:33
  • `List` is a [raw type](https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html) and ? is a [wildcard](https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html), And you should avoid using either of these unless you know EXACTLY what you're doing. (prefer `` where you can) – Tezra Jun 07 '17 at 14:43
  • @Radiodef function addConverter is provided by third party library I can not change it. I have to Implement class that implements Converter. Everything is fine when I use NonProblematicImplementation but I get compiler warning about raw List type. So I started to correct warrnings in my code. – Trismegistos Jun 07 '17 at 14:46

1 Answers1

1

Based on your comment, I think the right thing to do here might be to use an unchecked cast on List.class, but first, the reason the code in the question doesn't compile is roughly:

  • T of addConverter is inferred to be List<?>.
  • The bounded wildcard in cls requires that its type argument be T or a subtype of T, but the raw type List is a supertype of List<?> (specified here).
  • Therefore, Class<List> is incompatible with the inferred type of cls which is Class<? extends List<?>>.

So, for example, either of the following two declarations would compile with the invocations in the question:

<T> void m(Class<T> cls, Converter<? extends T> converter) {}
<T> void m(Class<? super T> cls, Converter<T> converter) {}

That of course doesn't help you out, but it illustrates the relationship between List and List<?>.

You might see also these two answers of mine which discuss similar situations.

So anyway, based on your comment saying that you're trying to eliminate raw types and can't change the declaration of addConverter, what could be appropriate is to use an unchecked cast from Class<List> to Class<List<?>>:

@SuppressWarnings("unchecked")
static final Class<List<?>> WILD_LIST =
    (Class<List<?>>) (Class<? super List<?>>) List.class;

This will let you call e.g.:

addConverter(WILD_LIST, new ProblematicConverter());

However, I'd like to point out that unchecked casting is not a general solution. It's a solution to this specific problem of converting e.g. a Class<GenericType> to a Class<GenericType<?>>. It's safe to do because GenericType<?> is a more restrictive type than the raw type GenericType and because Class has a very limited range of things it can do with its type argument.

If you could change the declaration of addConverter, I think I would recommend using something like Guava TypeToken instead of Class, because then you don't have this sort of problem.

Radiodef
  • 37,180
  • 14
  • 90
  • 125