27

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>)
    }
}
Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
  • I hope someone can explain this in English. I'm pretty sure it's related to the [Covariance](http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html) issue, meaning that assigning `ShapeSaver` to a generic `ShapeProcessor` could lead to a runtime exception. But I'm not certain of the details. – OverZealous Aug 14 '11 at 03:50
  • Yes I'm really curious to hear this one explained. Love the title. Echoes my experiences on the matter :) – Owen Aug 14 '11 at 03:52
  • +1 reminds me of this question I asked a while back http://stackoverflow.com/questions/6065998/how-to-declare-class-class-with-valid-generics - it may be a similar issue of information being lost through the use of `.class` – Paul Bellora Aug 14 '11 at 03:58
  • 2
    I love the title of this question. Someone could write a book with that title. – Marcelo Aug 14 '11 at 21:56

2 Answers2

18

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.

Community
  • 1
  • 1
irreputable
  • 44,725
  • 9
  • 65
  • 93
  • great - this answer deserves upvotes as it really builds on Bohemian's *how* and finally gets at the *why*. (unfortunately I was just vox populied) – Paul Bellora Aug 14 '11 at 21:53
  • @irreputable, thanks so much for the detailed explanation. This question, perhaps not so unsurprisingly, is actually the result of my use of Guice. I am really hoping I'm going to avoid all of this ugliness when I soon begin the jump to Scala. – Jeff Axelrod Aug 15 '11 at 02:06
  • @irreputable, if you wouldn't mind adding the appropriate Guice function declaration to your answer: genericsHell(TypeLiteral extends ShapeProcessor extends Shape>> typeLiteral) – Jeff Axelrod Aug 15 '11 at 02:33
11

Typing the genericsHell method allows it to compile:

static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) {}

EDITED: This allows the compiler to specify from context, or by coding an explicit type, that the ShapeProcessor is not literally any ShapeProcessor, but the same type as the one passed as a parameter. If the call was explicitly typed, (which the compiler does under the covers) the code would look like this:

MyClass.<ShapeSaver>genericsHell(ShapeSaver.class);

Which interestingly, gives a type warning, but still compiles. The explicit type is not required however because sufficient type information is available from the parameter to infer the generic type.


Your question is missing some declarations, so I added them in to create a Short Self-Contained Correct Example - ie this code compiles as-is

static interface Shape { }

static interface Circle extends Shape { }

static interface ShapeProcessor<T extends Shape> { }

static class CircleDrawer implements ShapeProcessor<Circle> { }

static class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }

static void genericsHeaven(ShapeProcessor<? extends Shape> a) { }

// The change was made to this method signature:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) { }

static void test() {
    genericsHeaven(new CircleDrawer());
    genericsHeaven(new ShapeSaver<Circle>());
    genericsHell(CircleDrawer.class);
    genericsHell(ShapeSaver.class);
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    So why is typing the method necessary? – Paul Bellora Aug 14 '11 at 04:27
  • 1
    +1 took me a while, but makes sense, esp. after reading the linked article. So typing the method allows the compiler to infer a raw type, which loses the nested type information that it cannot verify. To illustrate, after typing the method, the following call causes the original error: `MyClass.>genericsHell(ShapeSaver.class)` (note the nested `>`) – Paul Bellora Aug 14 '11 at 05:24
  • nice suggestion and especially for sscce +1 – mKorbel Aug 14 '11 at 09:04