I suspect this has been asked here (and answered) before, but I don't know how to name the problem. Why can I express the wildcards without problem only when I'm not passing the class itself?
It all boils down to this code. Everything works as expected except for the call to genericsHell(ShapeSaver.class)
:
interface Shape { } interface Circle extends Shape { } interface ShapeProcessor<T extends Shape> { } class CircleDrawer implements ShapeProcessor<Circle> { } class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { } class Test { void genericsHeaven(ShapeProcessor<? extends Shape> a) {} void genericsHell(Class<? extends ShapeProcessor<? extends Shape>> a) {} void test() { genericsHeaven(new CircleDrawer()); genericsHeaven(new ShapeSaver<Circle>()); genericsHell(CircleDrawer.class); genericsHell(ShapeSaver.class); // ERROR: The method genericsHell is not applicable for the arguments (Class<ShapeSaver>) } }
The type of ShapeSaver.class
is Class<ShapeSaver>
. When feed it to genericsHell()
, compiler needs to check if Class<ShapeSaver>
is a subtype of Class<? extends ShapeProcessor<?>
, which is reduces to whether ShapeSaver
is a subtype of ShapeProcessor<?>
. The subtype relation does not hold, the method call fails.
The same thing should be true for @Bohemian's solution. Here the subtype checking occurs at bound checking of T
after T
is inferred. It should fail too. This appears to be a compiler bug, which somehow misinterprets the rule that Raw
is assignable to Raw<X>
as if Raw
is a subtype of Raw<X>
. see also Enum.valueOf throws a warning for unknown type of class that extends Enum?
A simple solution to your problem is to declare
void genericsHell(Class<? extends ShapeProcessor> a)
indeed, ShapeSaver
is a subtype of ShapeProcessor
, and the call compiles.
That's not just a workaround. There's a good reason for it. Strictly speaking, for any Class<X>
, X
must be a raw type. For example, Class<List>
is ok, Class<List<String>>
is not. Because there is really no class that represents List<string>
; there is only a class representing List
.
Ignore the stern warning that you shall not use raw type. We must use raw types sometimes, given how Java type system is designed. Even Java's core APIs (Object.getClass()
) use raw types.
You probably intended to do something like this
genericsHell(ShapeSaver<Circle>.class);
Unfortunately, that's not allowed. Java could have, but did not, introduce type literal along with generics. That created lots of problems for lots of libraries. java.lang.reflect.Type
is a mess and unusable. Every library has to introduce their own representation of type system to solve the problem.
You can borrow one, e.g. from Guice, and you'll be able to
genericsHell( new TypeLiteral< ShapeSaver<Circle> >(){} ) ------------------
(learn to skip the craps around ShaveSaver<Circle>
when reading the code)
In the method body of genericsHell()
, you'll have full type information, not just the class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With