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:
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:
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
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.
B... On the other hand if I try to inject Reporting
as:
@Inject
Reporting<? extends ReportingEvent> reporting;
then IDEA states
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?
(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
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
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
Reporting<? extends ReportingEvent> reporting
.
In your code what if m.getEntity().getReportEvent()
returns a ColdReporting
? It is not compatible with what the report
method expects here (a HotReporting
).
Option 1:
I would try to get rid of the generic type parameters and define Reporting
as
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).HotReportingImpl
can get a ColdReporting
object as an argument.Option 2:
You can make the InternalService
generic by adding the type parameters to it.
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
).
Have a look at Typesafe heterogeneous containers by Joshua Bloch . 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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.