So we have a generic method like this, which is part of dependency injection initialisation:
public static <TS, TI extends TS> void registerTransient(
Class<TS> serviceClass, Class<TI> implementationClass)
{
//
}
At some point we found a case where a class might not necessarily be present. And it's an implementation class which we would be injecting multiple off (so the service class is the same as the implementation class.) Naturally you would write this like this:
Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz, clazz);
IDEA has no problems with this, but javac complains:
error: method registerTransient in class TestTrash cannot be applied to given types;
required: Class<TS>,Class<TI>
found: Class<CAP#1>,Class<CAP#2>
reason: inferred type does not conform to declared bound(s)
inferred: CAP#2
bound(s): CAP#1
where TS,TI are type-variables:
TS extends Object declared in method <TS,TI>registerTransient(Class<TS>,Class<TI>)
TI extends TS declared in method <TS,TI>registerTransient(Class<TS>,Class<TI>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
What gives? The method requires the second parameter to be a subclass of the first. Irrespective of what class ?
happens to be, it's the same class object for both parameters and a class is, I thought, always assignable from itself. It's almost as if javac is unnecessarily inventing a second wildcard type to use for the second parameter and then going "oh dear, you have two wildcards here, so I can't tell if one is assignable from the other."
The problem is that
Class<?>
cannot be cast to any other type unless via an explicit cast (it actually becomesClass<#capture-... of ?>
during capture conversion). As such, the compiler cannot (statically) match the type bounds of those captures with the parameterized types of the method definition.Try casting it explicitly to Class first, like in:
This way the compiler can bind
TS
toObject
andTI
to something that extends Object (it will still emit a warning, though).The fact that IntelliJ's compiler does not complain about this, might be due to some optimization or might even be a compiler bug. You should post it as such and wait for a reply.
If you wish to check it with a slightly different approach, the following will still not compile, even though it "looks" ok:
Have a look in here. It presents an extraordinary view of Java's type system (although quite dense).