当前位置: 首页 > 知识库问答 >
问题:

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

孟跃
2023-03-14

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

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

但是不能编译TestViewImFactory。错误消息是:

“类'TestViewImplFactory'必须声明为抽象或在'ViewFactory'中实现抽象方法创建”

Java说,以下是正确的:

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

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

  1. 两个自动生成的文件中有一个错误

因为他们俩真的很相似。我以为第一个是真的。

我错过了什么?

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

public final class TestViewImplFactory implements ViewFactory<TestView> {

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

问题出现了,构造函数参数(类型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个答案

慕仲渊
2023-03-14

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

因此,给定消息签名

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

陆俭
2023-03-14

为什么它不起作用:

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

此签名导致编译器在调用create()的位置推断S<代码>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

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

但是返回类型依赖于参数演示者。如果演示者为您提供了一个仅创建T实例的方法,这可能会起作用。

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

通过类型绑定方法的泛型意味着接口的实现提供了具体的类型。因此,对于一个对象,您不需要提供多个不同的绑定。无论您在哪里调用PresentFactory的创建()方法

 类似资料:
  • 我正在使用注释处理器来处理方法参数的注释。 用于参数的注释类型有一个注释@参数 现在,当注释处理器运行时,我想检查参数注释()是否有参数注释。我通过执行以下代码来实现这一点。 由于某种原因,arg始终为空。是否有注释未返回的原因?

  • 使用Java注释处理器,我有以下类型: 并且: 我有的为: 现在我想获取类型参数<code>的TypeElement 我试过了: 当我打印时,我得到而不是。 如何从TypeParameter中获取真正的StartPagePresenter?

  • 我正试图使Filepond工作,但CSS中的这一行似乎破坏了它-在ul选择器中。 我试着对页面的整个部分进行核化,直到Filepond起作用,将目标锁定在css上,最后在ul{}中找到前面提到的行。我可以把其他的东西都抹掉,只留下那条线,而文件孔仍然坏了,所以我肯定这是问题所在,但我不知道是怎么回事。 我尝试使用Chrome的检查器功能查看运行时页面源代码,但在那里找不到溢出。 然后我使用Note

  • 我试图使用IntelliJ2017 Ultimate构建/运行一个使用MapStruct的Spring Boot应用程序。这是一个分级项目。我的问题是IntelliJ似乎没有运行MapStruct注释处理器。我意识到我可以配置IntelliJ来委托Gradle构建过程(请参见此),但我希望简单地配置IntelliJ来使用APT来生成必要的类本身。 我已经为我的项目启用了APT,但是我的类仍然没有生

  • 目前,我们所有的构建都失败了,因为无法从公共SBT插件回购中解析插件。我们得到以下错误: [警告]注意:某些未解析的依赖项具有额外属性。检查这些依赖项是否与请求的属性一起存在。[警告]com。类型安全。播放:sbt插件:2.4。0(scalaVersion=2.10,sbtVersion=0.13)[警告]com。github。gseitz:sbt版本:1.0。0(标度规避=2.10,sbtVer

  • 需要注释处理器的帮助。我创建了一个简单的注释处理器,它使用@autoservice注释来检查注释的字段是否为最终字段。但它没有显示任何编译时错误。这是我的配置 注释: 注释处理器: pom文件: 测试文件: