0

What is wrong with Class A below that won't allow it to compile?

public class GenericsHell {
   interface Shape{} 
   interface Circle extends Shape {} 

   interface ShapeHelper<T extends Shape> {
      void draw(T shape);
   }

   class A<T extends Shape> {
      A(T shape, ShapeHelper<? extends Shape> helper) {
         helper.draw(shape); // Issues not-applicable argument error 
      }
   }

   class B {
      B(Circle circle, ShapeHelper<Circle> helper) {
         helper.draw(circle);
      }
   }
}   

Eclipse gives the following error:

The method draw(capture#1-of ? extends Shape) in the type ShapeHelper<capture#1-of ? extends Shape> is not applicable for the arguments (T)

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
  • 1
    Where is that `Integer` part of the error coming from ? I do not see that in your code – Robin Jan 22 '12 at 15:17
  • @Robin, apparently Eclipse doesn't update its "problems" tab unless you compile. Eclipse was already giving me goofy quick-fix reports, and since the error made no sense, I assumed it was some kind of Eclipse issue. The accepted answer makes complete sense and I don't know why I didn't notice the bug myself even without a sensible error. – Jeff Axelrod Jan 22 '12 at 15:57

5 Answers5

3

You defined your generic parameter for Class A as one thing, but then tried to use it in an incompatible fashion in your constructor (<T extends Shape> is not the same thing as <? extends Shape>. To get your code to compile change it to consistently use your already defined generic parameter:

class A<T extends Shape> {
    public A(T shape, ShapeHelper<T> helper) {
        helper.draw(shape);
    }
}

As an aside, your code doesn't generate the error message you showed in your question. Rather it would be something more like this:

The method draw(capture#1-of ? extends GenericsHell.Shape) in the type GenericsHell.ShapeHelper is not applicable for the arguments (T)

Perception
  • 79,279
  • 19
  • 185
  • 195
2

The method draw(capture#1-of ? extends GenericsHell.Shape) in the type GenericsHell.ShapeHelper<capture#1-of ? extends GenericsHell.Shape> is not applicable for the arguments (T)

The problem is that in your declaration, shape is of type T, but you request a ShapeHelper of type <? extends Shape> which means that one could pass as argument a ShapeHelper where S and T are distinct.

You would then call helper<S>.draw(shape<T>); which doesn't make sense.

A correct implementation for this method would be:

class A<T extends Shape> {
  A(T shape, ShapeHelper<T> helper) {
    helper.draw(shape); 
  }
}

Which ensures that the shape and the shape helper are of compatible types.

ARRG
  • 2,476
  • 17
  • 28
1

Would be important to see your call to A. But it seems like you did something like A<Integer>. But T must extend Shape according to your class declaration.. and Integer doesn't. So either change <? extends Shape> to <T> or provide a type that is a Shape to A

hage
  • 5,966
  • 3
  • 32
  • 42
0

Try this instead:

class A<T extends Shape> {
    A(T shape, ShapeHelper<T> helper) {
        helper.draw(shape);
    }
}
Óscar López
  • 232,561
  • 37
  • 312
  • 386
0

Remember PECS (producer extends, consumer super).

helper is a consumer (you pass something to it), hence it cannot be extends. Perhaps it could be super, but I don't know if that makes sense in this case

newacct
  • 119,665
  • 29
  • 163
  • 224
  • Sorry, I just named it `ShapeHelper` hastily as an example. I agree the choice of sample could have been better. Maybe instead it could have been `Polygon { void getNumberOfSides(T shape); }` Incidentally, your answer should really be deleted and re-entered as a comment. It doesn't answer why the code doesn't compile. – Jeff Axelrod Jan 23 '12 at 05:04