我得到了一个通用接口,其中有一个接受通用类型参数的方法:

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


此外,我还有一个抽象类,该类使用有界通配符声明此接口类型的变量:

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
}


还有一个子类,它为componentRenderer设置了一个实现:

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


FlatButtonRenderer实现为:

public class FlatButtonRenderer implements ComponentRenderer<GuiButton> {

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


我看不到哪里出错了,但是GuiComponent中的componentRenderer.draw(this)调用不适用于以下错误:

java - 调用通用接口(interface)方法不起作用-LMLPHP

据我了解,它告诉我,我不能使用GuiComponent,因为它不是从GuiComponent派生的,这没有任何意义。我也尝试过? super GuiComponent,它将接受draw()调用,但是不接受FlatButtonRenderer的实现。

我不理解此语法错误,是否有人知道我该如何更改代码?

编辑:
当我在调用draw()时使用我的IDE的代码完成时,它说我说draw接受一个类型为“ null”的参数,因此由于某种原因,它无法弄清楚该参数应为哪种类型。 ..

最佳答案

问题在于? extends GuiComponent的意思是“ GuiComponent的一个特定子类型,但不知道哪个”。

编译器不知道thisGuiComponent的正确ComponentRenderer子类型。可能是渲染器只能与其他某些特定子类一起使用。

您必须使用某种自类型模式来以类型安全的方式执行此操作。这样,您就可以将渲染器的类型变量与GuiComponent子类的类型“连接”在一起。

例:

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;
    }
}


我最初将其称为curiously recurring template patternThis answer进一步说明。

09-27 03:18