[英]Binding generic service with its implementation and subtypes
Disclaimer: Bear with me for a good amount of code to explain the scenario.免责声明:请耐心等待我提供大量代码来解释该场景。
In one of the maven modules (core), we have the following classes:在其中一个 maven 模块(核心)中,我们有以下类:
abstract class ReportingEvent {
}
abstract class Element {
public <E extends ReportingEvent> Optional<E> getReportEvent() {
return Optional.empty();
}
}
services such as:服务,例如:
public interface Reporting<E extends ReportingEvent> {
void report(E event);
}
interface InternalService {
}
public class InternalServiceImpl implements InternalService {
@Inject
Reporting<ReportingEvent> reporting; // 1. Possible to use a generic type? How?
private void reportEvents(BatchRequest batchRequest) {
batchRequest.stream()
// section below is of importance
.map(m -> m.getEntity().getReportEvent()) // the generic method from 'Element'
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(event -> reporting.report(event)); // method from 'Reporting'
}
}
class CoreBindingModule extends AbstractModule {
protected void configure() {
bind(InternalService.class).to(InternalServiceImpl.class).in(Singleton.class);
}
}
Further in another maven module(consumer) that we deploy, we have classes related to and implementing the above as:此外,在我们部署的另一个 maven 模块(消费者)中,我们有与上述相关并实现上述内容的类:
abstract class BaseReporting extends ReportingEvent {
}
class ColdReporting extends BaseReporting {
}
abstract class Node extends Element {
}
class Cold extends Node {
@Override
public Optional<ColdReporting> getReportEvent() {
return Optional.ofNullable(new ColdReporting()); // some business logic
}
}
class ReportingImpl implements Reporting<ReportingEvent> { // 2. Use 'BaseReporting' here
void report(ReportingEvent event){}
}
class ConsumerBindingModule extends AbstractModule {
protected void configure() {
bind(new TypeLiteral<Reporting<ReportingEvent>>() {}).to(ReportingImpl.class).in(Singleton.class);
}
}
The above code works fine.上面的代码工作正常。 But the problem is the use of types that don't quite relate to the modules.
但问题是使用了与模块不太相关的类型。
A... So if I change the binding in the consumer module to A...所以如果我将消费者模块中的绑定更改为
bind(new TypeLiteral<Reporting<BaseReporting>>() {}).to(ReportingImpl.class).in(Singleton.class);
with和
class ReportingImpl implements Reporting<BaseReporting> {
void report(BaseReporting event){}
}
I get an error我收到一个错误
No implementation for Reporting<ReportEvent> was bound. while locating Reporting<ReportEvent> for field at InternalServiceImpl.reporting(InternalServiceImpl.java:21)
which is relevant and I cannot make use of Reporting<BaseReporting>
in the core module anyway.这是相关的,无论如何我都无法在核心模块中使用
Reporting<BaseReporting>
。
B... On the other hand if I try to inject Reporting
as: B...另一方面,如果我尝试将
Reporting
注入为:
@Inject
Reporting<? extends ReportingEvent> reporting;
then IDEA states然后 IDEA 声明
Required type: capture of ? Provided: ReportingEvent
on the line在线上
...forEach(event -> reporting.report(event))
Is there a way to get around this situation while trying to solve for 1 and 2 as mentioned in the code sections?在尝试解决代码部分中提到的1和2 时,有没有办法解决这种情况?
(I might be restating what you already know for the most part) (我可能会在很大程度上重申您已经知道的内容)
Keeping aside your Guice related modules and configuration, your problem can be written down as撇开与 Guice 相关的模块和配置,您的问题可以写成
Reporting<ReportingEvent> reportingEvent = new ReportingImpl();
Reporting<? extends ReportingEvent> reporting = baseReporting;
reporting.report(reportingEvent); //won't work
which is identical to这与
List<? extends String> list = new ArrayList<>();
list.add("a"); //won't work
Say, you have a HotReporting
class that extends BaseReporting
, thus you have something like this说,你有一个扩展
BaseReporting
的HotReporting
类,因此你有这样的东西
public class ReportingImpl implements Reporting<ReportingEvent> {
public void report(ReportingEvent event){}
}
public class ColdReportingImpl implements Reporting<ColdReporting> {
public void report(ColdReporting event){}
}
public class HotReportingImpl implements Reporting<HotReporting> {
public void report(HotReporting event){}
}
Assume if you injected a HotReportingImpl
for the field Reporting<? extends ReportingEvent> reporting
假设您
HotReportingImpl
为字段Reporting<? extends ReportingEvent> reporting
注入了一个HotReportingImpl
Reporting<? extends ReportingEvent> reporting
Reporting<? extends ReportingEvent> reporting
. Reporting<? extends ReportingEvent> reporting
。
In your code what if m.getEntity().getReportEvent()
returns a ColdReporting
?在您的代码中,如果
m.getEntity().getReportEvent()
返回ColdReporting
怎样? It is not compatible with what the report
method expects here (a HotReporting
).它与这里的
report
方法所期望的不兼容(一个HotReporting
)。
Option 1:选项1:
I would try to get rid of the generic type parameters and define Reporting
as我会尝试摆脱泛型类型参数并将
Reporting
定义为
public interface Reporting {
void report(ReportingEvent event);
}
public class BaseReportingImpl implements Reporting {
@Override
public void report(ReportingEvent event) {
}
}
//... and so on
Problems:问题:
ReportingEvent
in the implementation (need typecasts which is not good).ReportingEvent
的确切子类型(需要不好的类型转换)。HotReportingImpl
can get a ColdReporting
object as an argument.HotReportingImpl
可以获取ColdReporting
对象作为参数。 Option 2:选项 2:
You can make the InternalService
generic by adding the type parameters to it.您可以通过向其添加类型参数来使
InternalService
泛型。
If the classes implementing Reporting
have to deal with concrete types of ReportingEvent
, then it doesn't seem right for Element
to return the base type ( ReportingEvent
).如果实现类
Reporting
必须处理的具体类型ReportingEvent
,那么它看起来不正确的Element
返回基本类型( ReportingEvent
)。
Have a look at Typesafe heterogeneous containers by Joshua Bloch .看看Joshua Bloch 的 Typesafe 异构容器。 Your problem overlaps with that.
你的问题与此重叠。 You can maintain a mapping from the type of
ReportingEvent
to the corresponding subclass of Reporting
with that type argument.您可以使用该类型参数维护从
ReportingEvent
的类型到相应的Reporting
子类的映射。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.