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

时间:2018-08-30 12:44:40

标签: java generics annotations annotation-processing

背景

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

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

考虑到用户想知道的结果,这根本不起作用,将返回哪个View,以及需要哪个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);
    }
}

出现问题,即构造函数Parameter(类型为TestPresenter)不正确。同样,将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中)自动继承另一个泛型?

2 个答案:

答案 0 :(得分:15)

为什么不起作用:

tm_isdst

此签名使编译器在调用-1的位置推断public interface PresenterFactory<T extends View> { <S extends Presenter<T>> S create(); } S就是您将create()分配给的对象,如下所示:

S

这意味着:

create()

不是以下实现:

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); } 为您提供了仅创建presenter实例的方法,则可能会起作用。

其他解决方案为何起作用:

通过类型绑定方法的泛型意味着接口的实现提供了具体的类型。因此,对于一个对象,您不需要提供多个不同的绑定。无论您在何处调用presenter的{​​{1}}方法,返回类型的泛型都绑定到T。因此,create()的每个子类型都有可能的实现。

答案 1 :(得分:0)

我认为应该解决问题陈述的第一部分,因为我注意到注释处理器正在实现原始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) { ... }
}

但是,无论使用哪种方法,都不能将参数限制为TestPresenter。您必须将ViewFactory更改为

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

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