注释处理器似乎破坏了 Java 泛型背景问题

背景

我试图使用注释处理器来生成特定工厂接口的实现。这些接口如下所示:

public interface ViewFactory<T extends View> {

    <S extends Presenter<T>> T create(S presenter);

}

public interface PresenterFactory<T extends View> {

    <S extends Presenter<T>> S create();

}

注释处理器正在执行正确的操作,并为每个匹配的类生成一个工厂,该工厂使用相应的注释进行注释。

问题

注释处理器的输出如下:

public final class TestViewImplFactory implements ViewFactory {

    public final TestView create(TestPresenter presenter) {
        return new TestViewImpl(presenter);
    }
}

和相应的其他类:

public final class TestPresenterImplFactory implements PresenterFactory {

    public final TestPresenter create() {
        return new TestPresenterImpl();
    }
}

然而,TestViewImplFactory不能被编译。错误消息是:

“类'TestViewImplFactory'必须在'ViewFactory'中声明为抽象或实现抽象方法 create(S)”

Java说,以下是正确的:

@Override
public View create(Presenter presenter) {
    return new TestViewImpl(presenter);
}

考虑到用户想知道,将返回哪个视图以及需要哪个演示者,这根本不起作用。我本来以为:

  1. 两个自动生成的文件都是错误的
  2. 或两者都是正确的

因为它们都非常相似。我以为第一个是真的。

我在这里错过了什么?


如果我将泛型类型添加到 TestViewImplFactory 中,如下所示:

public final class TestViewImplFactory implements ViewFactory<TestView> {

    @Override
    public <S extends Presenter<TestView>> TestView create(S presenter) {
        return new TestViewImpl(presenter);
    }
}

问题出现了,构造函数参数(属于类型测试预感器)不正确。将 S 更改为具体的 TestPresenter 将再次使类不可编译,原因与上述相同。


因此,我偶然发现了一个可以编译的“解决方案”。

基本上必须做的是将ViewFactory界面更改为以下内容:

public interface ViewFactory<T extends View, S extends Presenter<T>> {

    T create(S presenter);

}

因此,类定义具有与上述问题中的方法相同的泛型类型。

编译后(这次使用泛型类型规范),输出如下所示:

public final class TestViewImplFactory implements ViewFactory<TestView, TestPresenter> {
    public TestViewImplFactory() {
    }

    public final TestView create(TestPresenter presenter) {
        return new TestViewImpl(presenter);
    }
}

这可以编译并成功运行。

然而,这并没有回答最初的问题。为什么类型定义中显式声明的泛型是正确的,但在方法声明中继承指定的泛型是错误的并且不可编译?

具体来说:为什么Java可以自动继承一个泛型(在PresenterFactory中)而其他泛型不能(在ViewFactory中,在方法和类型声明中)?


答案 1

为什么它不起作用:

public interface PresenterFactory<T extends View> {
    <S extends Presenter<T>> S create();
}

此签名使编译器在调用的位置进行推断。 将是您分配到的内容,例如:Screate()Screate()

FancyPresenter fp = presenterFactory.create();
SomeOtherPresenter sop = presenterFactory.create();

这意味着:

public TestPresenter create(){...}

不是以下各项的实现:

<S extends Presenter<T>> S create();

但是一个方法覆盖。没有接口方法的实现。甚至不可能为任何实现提供具体的.它类似于:S

public interface ViewFactory<T extends View> {
    <S extends Presenter<T>> T create(S presenter);
}

这里再次在方法调用上推断出泛型。因此,实现必须接受 的每个子类型。唯一有效的实现是:Presenter<T>

public interface ViewFactory<T extends View> {
    T create(Presenter<T> presenter);
}

但是返回类型依赖于参数 。如果为您提供了仅创建 实例的方法,则此方法可能有效。presenterpresenterT

为什么其他解决方案有效:

通过类型绑定方法的泛型意味着接口的实现提供了具体的类型。因此,对于一个对象,您不需要提供多个不同的绑定。无论您在哪里调用返回类型泛型的方法都绑定到 。因此,对于 的每个子类型都有一个可能的实现。create()PresenterFactory<TestView, TestPresenter<TestView>>TestPresenter<TestView>PresenterFactory<...>


答案 2

我认为您的问题陈述的第一部分应该得到解决,因为我注意到您的注释处理器正在实现原始ViewFactory类型。我想对于类型擦除,因为它是生成的代码,所以在实践中并没有真正的区别。但是,如果处理器可以使用参数化类型生成实现,那么至少更容易推断出问题。

因此,给定一个消息签名,您可以让它生成:<S extends Presenter<T>> T create(S presenter)

public class TestViewImplFactory implements ViewFactory<TestView> {
  @Override
  public <S extends Presenter<TestView>> TestView create(S presenter) { ... }
}

或者更小的一点:

public class TestViewImplFactory implements ViewFactory<TestView> {
  @Override
  public TestView create(Presenter presenter) { ... }
}

但是,对于其中任何一个,您都无法将参数限制为 。您必须更改为类似TestPresenterViewFactory

public interface ViewFactory<T extends View, U extends Presenter<T>>

他们实现了.您必须在实现中使用类型参数来实现所需的类型限制。ViewFactory<TestView, TestPresenter>


推荐