In converting some Java code, I wanted to translate something like
public class TemplatedType<T> { /* ... */ }
// In some other class:
public List<ReturnType> foo(Class<? extends TemplatedType> subType) { /* ... */ }
public List<ReturnType> foo(Collection<Class<? extends TemplatedType>> subTypes) { /* ... */ }
with a use-site variance like (for the function using collections)
List<? extends TemplatedType> typeList = /* Init code */ ;
foo(typeList);
My first pass matched the Intellij auto-conversion:
fun foo(subType: Class<out TemplatedType<*>>): List<ReturnType> { /* ... */ }
fun foo(subTypes: Collection<Class<out TemplatedType<*>>>): List<ReturnType> { /* ... */ }
Interestingly, usages of the former (single class arg) worked perfectly while the latter failed building as it was unable to convert the type, something like:
error: no suitable method found for foo(List<Class<? extends TemplatedType>>)
method foo(Class<? extends TemplatedType<?>>) is not applicable (argument mismatch; List<Class<? extends TemplatedType>> cannot be converted to Class<? extends TemplatedType<?>>) method foo(Collection<? extends Class<? extends TemplatedType<?>>>) is not applicable (argument mismatch; List<Class<? extends TemplatedType>> cannot be converted to List<? extends Class<? extends TemplatedType<?>>>)
I ended up using upper bounds, which worked (and makes sense):
fun <T : TemplatedType<*>> foo(subType: Class<out T>): List<ReturnType> { /* ... */ }
fun <T : TemplatedType<*>> foo(subTypes: Collection<Class<out T>>): List<ReturnType> { /* ... */ }
However I don’t fully understand the compatibility issue or the difference in bounding. I took a look at the Variant Generics section of the docs, in addition to the obvious Generics section, but still feel I’m missing something.
Can someone break this down a little for me?