注释处理器似乎打破了Java泛型

Tho*_*uck 20 java generics annotations annotation-processing

背景

我试图使用Annotation Processors来生成特定Factory接口的实现.这些接口看起来如下:

public interface ViewFactory<T extends View> {

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

}
Run Code Online (Sandbox Code Playgroud)

public interface PresenterFactory<T extends View> {

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

}
Run Code Online (Sandbox Code Playgroud)

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

问题

注释处理器的输出如下:

public final class TestViewImplFactory implements ViewFactory {

    public final TestView create(TestPresenter presenter) {
        return new TestViewImpl(presenter);
    }
}
Run Code Online (Sandbox Code Playgroud)

和相应的其他类:

public final class TestPresenterImplFactory implements PresenterFactory {

    public final TestPresenter create() {
        return new TestPresenterImpl();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,TestViewImplFactory无法编译.错误消息是:

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

Java说,以下是正确的:

@Override
public View create(Presenter presenter) {
    return new TestViewImpl(presenter);
}
Run Code Online (Sandbox Code Playgroud)

考虑到用户想要知道哪些View将被返回以及需要哪个Presenter,这根本不起作用.我原以为:

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

因为他们两个都很相似 我预计第一个是真的.

我在这里错过了什么?


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

public final class TestViewImplFactory implements ViewFactory<TestView> {

    @Override
    public <S extends Presenter<TestView>> TestView create(S presenter) {
        return new TestViewImpl(presenter);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题出现了,构造函数Parameter(类型为TestPresenter)不正确.将S更改为具体的TestPresenter将再次使该类无法编译,原因与上述相同.


所以,我偶然发现了一个可以编译的"解决方案".

基本上要做的是将ViewFactory接口更改为以下内容:

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

    T create(S presenter);

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

    public final TestView create(TestPresenter presenter) {
        return new TestViewImpl(presenter);
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以编译并成功运行.

然而,这并不回答原始问题.为什么在类型定义中明确声明的泛型是正确的,但在方法声明中继承指定错误且不可编译?

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

Tri*_*ova 15

为什么它不起作用:

public interface PresenterFactory<T extends View> {
    <S extends Presenter<T>> S create();
}
Run Code Online (Sandbox Code Playgroud)

此签名使编译器Screate()调用的位置进行推断.S将是你所分配create()的:

FancyPresenter fp = presenterFactory.create();
SomeOtherPresenter sop = presenterFactory.create();
Run Code Online (Sandbox Code Playgroud)

这意味着:

public TestPresenter create(){...}
Run Code Online (Sandbox Code Playgroud)

不是以下的实现:

<S extends Presenter<T>> S create();
Run Code Online (Sandbox Code Playgroud)

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

public interface ViewFactory<T extends View> {
    <S extends Presenter<T>> T create(S presenter);
}
Run Code Online (Sandbox Code Playgroud)

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

public interface ViewFactory<T extends View> {
    T create(Presenter<T> presenter);
}
Run Code Online (Sandbox Code Playgroud)

但返回类型取决于参数presenter.如果presenter为您提供T仅创建实例的方法,则可能会有效.

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

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