注释处理器似乎破坏了 Java 泛型
Annotation Processor appears to break Java generics
背景
我试图使用注释处理器来生成特定工厂接口的实现。这些界面如下所示:
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();
}
注释处理器正在做正确的事情,并为每个匹配 class 生成一个工厂,并用相应的注释进行注释。
问题
注释处理器的输出如下:
public final class TestViewImplFactory implements ViewFactory {
public final TestView create(TestPresenter presenter) {
return new TestViewImpl(presenter);
}
}
和对应的其他class:
public final class TestPresenterImplFactory implements PresenterFactory {
public final TestPresenter create() {
return new TestPresenterImpl();
}
}
但是无法编译 TestViewImplFactory。错误信息是:
"Class 'TestViewImplFactory' must be declared abstract or implement
abstract method create(S) in 'ViewFactory'"
Java 表示,下列正确的是:
@Override
public View create(Presenter presenter) {
return new TestViewImpl(presenter);
}
这根本行不通,考虑到用户想知道,将返回哪个View,需要哪个Presenter。我本以为:
- 要么两个自动生成的文件都是错误的
- 或两者都正确
因为他们真的很相似。我以为第一个是真的。
我在这里错过了什么?
如果我像这样将通用类型添加到 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 将再次使 class 不可编译,原因与上述相同。
所以,我偶然发现了一个可以编译的"solution"。
基本上必须做的是将 ViewFactory 接口更改为以下内容:
public interface ViewFactory<T extends View, S extends Presenter<T>> {
T create(S presenter);
}
因此 class 定义具有与上述问题中的方法相同的通用类型。
编译后(这次使用泛型类型规范),输出如下所示:
public final class TestViewImplFactory implements ViewFactory<TestView, TestPresenter> {
public TestViewImplFactory() {
}
public final TestView create(TestPresenter presenter) {
return new TestViewImpl(presenter);
}
}
这个可以编译运行成功
但这并没有回答原来的问题。为什么类型定义中明确声明的泛型是正确的,而方法声明中指定的继承和错误且不可编译?
具体来说:为什么 Java 可以自动继承一个泛型(在 PresenterFactory 中)而另一个不能(在 ViewFactory 中,在方法和类型声明中)?
为什么不起作用:
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<T>
的每个子类型。唯一有效的实现是:
public interface ViewFactory<T extends View> {
T create(Presenter<T> presenter);
}
但是 return 类型依赖于参数 presenter
。如果 presenter
为您提供了一种仅创建 T
实例的方法,这可能会起作用。
为什么其他解决方案有效:
通过类型绑定方法的泛型意味着接口的实现提供具体类型。因此,对于一个对象,您不需要提供多个不同的绑定。无论您在哪里调用 PresenterFactory<TestView, TestPresenter<TestView>>
的 create()
方法,return 类型的泛型都绑定到 TestPresenter<TestView>
。因此 PresenterFactory<...>
.
的每个子类型都有可能的实现
我认为您的问题陈述的第一部分应该得到解决,因为我注意到您的注释处理器正在实现原始 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>
。您有点必须在实现中使用类型参数来实现您想要的类型限制。
背景
我试图使用注释处理器来生成特定工厂接口的实现。这些界面如下所示:
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();
}
注释处理器正在做正确的事情,并为每个匹配 class 生成一个工厂,并用相应的注释进行注释。
问题
注释处理器的输出如下:
public final class TestViewImplFactory implements ViewFactory {
public final TestView create(TestPresenter presenter) {
return new TestViewImpl(presenter);
}
}
和对应的其他class:
public final class TestPresenterImplFactory implements PresenterFactory {
public final TestPresenter create() {
return new TestPresenterImpl();
}
}
但是无法编译 TestViewImplFactory。错误信息是:
"Class 'TestViewImplFactory' must be declared abstract or implement abstract method create(S) in 'ViewFactory'"
Java 表示,下列正确的是:
@Override
public View create(Presenter presenter) {
return new TestViewImpl(presenter);
}
这根本行不通,考虑到用户想知道,将返回哪个View,需要哪个Presenter。我本以为:
- 要么两个自动生成的文件都是错误的
- 或两者都正确
因为他们真的很相似。我以为第一个是真的。
我在这里错过了什么?
如果我像这样将通用类型添加到 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 将再次使 class 不可编译,原因与上述相同。
所以,我偶然发现了一个可以编译的"solution"。
基本上必须做的是将 ViewFactory 接口更改为以下内容:
public interface ViewFactory<T extends View, S extends Presenter<T>> {
T create(S presenter);
}
因此 class 定义具有与上述问题中的方法相同的通用类型。
编译后(这次使用泛型类型规范),输出如下所示:
public final class TestViewImplFactory implements ViewFactory<TestView, TestPresenter> {
public TestViewImplFactory() {
}
public final TestView create(TestPresenter presenter) {
return new TestViewImpl(presenter);
}
}
这个可以编译运行成功
但这并没有回答原来的问题。为什么类型定义中明确声明的泛型是正确的,而方法声明中指定的继承和错误且不可编译?
具体来说:为什么 Java 可以自动继承一个泛型(在 PresenterFactory 中)而另一个不能(在 ViewFactory 中,在方法和类型声明中)?
为什么不起作用:
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<T>
的每个子类型。唯一有效的实现是:
public interface ViewFactory<T extends View> {
T create(Presenter<T> presenter);
}
但是 return 类型依赖于参数 presenter
。如果 presenter
为您提供了一种仅创建 T
实例的方法,这可能会起作用。
为什么其他解决方案有效:
通过类型绑定方法的泛型意味着接口的实现提供具体类型。因此,对于一个对象,您不需要提供多个不同的绑定。无论您在哪里调用 PresenterFactory<TestView, TestPresenter<TestView>>
的 create()
方法,return 类型的泛型都绑定到 TestPresenter<TestView>
。因此 PresenterFactory<...>
.
我认为您的问题陈述的第一部分应该得到解决,因为我注意到您的注释处理器正在实现原始 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>
。您有点必须在实现中使用类型参数来实现您想要的类型限制。