Cydhra
Cydhra

Reputation: 581

Calling a Generic Interface Method does not work

I got a generic interface with one method accepting a parameter of the generic type:

public interface ComponentRenderer<T extends GuiComponent> {
    public void draw(T component);
}

Furthermore I have an abstract class, that declares a variable of this interface type using a bounded wildcard:

public abstract class GuiComponent extends Gui {
    private ComponentRenderer<? extends GuiComponent> componentRenderer;

    public void draw() {
        this.componentRenderer.draw(this);
    }

    //and a setter and getter for the ComponentRenderer
}

And a subclass, wich set a implementation for the componentRenderer:

public class GuiButton extends GuiComponent {
    public GuiButton(/* ... */) {
        //...
        this.setComponentRenderer(new FlatButtonRenderer());
    }

where FlatButtonRenderer is implemented as:

public class FlatButtonRenderer implements ComponentRenderer<GuiButton> {

    @Override
    public void draw(final GuiButton component) {
        //...
    }
}

I can't see where I got something wrong, but the call componentRenderer.draw(this) in GuiComponent does not work with the following error:

enter image description here

As far as I understand this, it says me, that I can't use GuiComponent because it does not derive from GuiComponent, what makes no sense. I've also tried ? super GuiComponent, which will accept the draw() call, but then does not accept the implementation of FlatButtonRenderer

I do not understand this syntax error, does anyone have an idea, how I need to change the code?

EDIT: When I use my IDE's code completion on the call of draw(), it says me, that draw accept one argument of type "null", so for some reason, it is not able to figure out, wich type the argument should be...

Upvotes: 2

Views: 1300

Answers (2)

Lii
Lii

Reputation: 12131

The problem is that ? extends GuiComponent means "one specific subtype of GuiComponent, but unknown which".

The compiler does not know that this is of the right GuiComponent subtype for the ComponentRenderer. It could be that the renderer only can work with some other specific subclass.

You have to use some kind of self-type pattern to do this in a type-safe way. That way you kind of "connect" the type variable of the renderer with the type of the GuiComponent subclass.

Example:

class Gui {}

interface ComponentRenderer<T extends GuiComponent<T>> {
    public void draw(T component);
}

// T is the self-type. Subclasses will set it to their own type. In this way this class
// can refer to the type of its subclasses.
abstract class GuiComponent<T extends GuiComponent<T>> extends Gui {
    private ComponentRenderer<T> componentRenderer;

    public void draw() {
        this.componentRenderer.draw(thisSub());
    }

    public void setComponentRenderer(ComponentRenderer<T> r) {}

    // This method is needed for the superclass to be able to use 'this'
    // with a subclass type. Sub-classes must override it to return 'this'
    public abstract T thisSub();

    //and a setter and getter for the ComponentRenderer
}

// Here the self-type parameter is set
class GuiButton extends GuiComponent<GuiButton> {
    public GuiButton(/* ... */) {
        //...
        this.setComponentRenderer(new FlatButtonRenderer());
    }

    class FlatButtonRenderer implements ComponentRenderer<GuiButton> {
        @Override
        public void draw(final GuiButton component) {
            //...
        }
    }

    @Override
    public GuiButton thisSub() {
        return this;
    }
}

This is originally (I think) called the curiously recurring template pattern. This answer explains it more.

Upvotes: 2

eclipz905
eclipz905

Reputation: 147

In GuiComponent, change your declaration of componentRenderer to this:

ComponentRenderer<GuiComponent> componentRenderer;

Upvotes: 0

Related Questions