Suzan Cioc
Suzan Cioc

Reputation: 30097

How to call generic method of wildcard type in Java?

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

Answers (3)

Konstantin Yovkov
Konstantin Yovkov

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

esel
esel

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

Dmitry Zaytsev
Dmitry Zaytsev

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

Related Questions