Reputation: 30097
I found I can't call generic methods of wildcard types and don't understand why?
public class GenericsTry2 {
public static class Element {
private Container<? extends Element> container;
public Container<? extends Element> getContainer() {
return container;
}
public void setContainer(Container<? extends Element> container) {
this.container = container;
}
public void doStuff() {
getContainer().doStuff(this); // how to call this?
}
}
public static class SomeSubElement extends Element {
}
public static class SomeSubElement2 extends Element {
}
public static class Container<E extends Element> {
public void doStuff(E element) {
}
}
public static void main(String[] args) {
Container<SomeSubElement2> c = new Container<SomeSubElement2>();
Element e = new SomeSubElement();
c.doStuff((SomeSubElement2) e); // still can do this at compile time this way
}
}
Upvotes: 4
Views: 1064
Reputation: 62864
Having Container<? extends Element>
means that the Container
can only produce Element
(s), but cannot consume Element
(s).
The reason for this is that ? extends Element
denotes a whole family of unknown sub-types of Element
. Let's assume you set your container to Container<SomeSubElement>
. Then, passing this
to the container (even you know it's an Element
, or a sub-type of Element
) won't be correct, because this
may be or may not be SomeSubElement
(depends on the Runtime type).
In the world of Generics, this is called co-variance.
In order to have the code compiling (I don't guarantee that you need exactly this), you can do (note that I've changed the container to be consumer of Element
(s), instead of producer):
public class Element {
private Container<? super Element> container;
public Container<? super Element> getContainer() {
return container;
}
public void setContainer(Container<? super Element> container) {
this.container = container;
}
public void doStuff() {
getContainer().doStuff(this);
}
}
However, if you need your Container
to be a producer and a consumer at the same time, just get rid of the wildcard and parameterize it with <Element>
only.
Upvotes: 9
Reputation: 983
The getContainer()
Method guarantees to return some object of a class that extends Element. So the compiler does not know at this point that the returned value is actually of type Container
. If you want to call the doStuff()
-Method of the Container
class, you have to cast the result of getContainer()
to the type Container
explicitly.
((Container) getContainer()).doStuff(this);
Upvotes: 0
Reputation: 23962
Container<E extends Element>
means that it contains something E
that extends from Element
. Not necessarily the Element
itself.
Imagine what would happen if you'll have:
Container<RedElement> redElements = new Container<RedElement>();
Container<E extends Element> container = redElements;
// According to your logic it will work
container.add(new Element());
// Problem here. You just put Element there which is not RedElement.
RedElement element = container.getFirst();
Upvotes: 1