Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

    }


}
like image 779
Suzan Cioc Avatar asked Dec 24 '22 16:12

Suzan Cioc


2 Answers

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.

like image 60
Konstantin Yovkov Avatar answered Jan 04 '23 17:01

Konstantin Yovkov


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();
like image 23
Dmitry Zaytsev Avatar answered Jan 04 '23 17:01

Dmitry Zaytsev