Java 8 Stream and common interface in the chain of responsibility
In a Spring Boot application running in Java 8, I want to implement common behavior to handle different types of commands.
The code below shows the solution I had in mind.
Generic processor pattern
public interface Processor<C extends Command> {
TypeCommand getCommandType();
void processCommand(C command);
}
public abstract class AbstractProcessor<C extends Command> implements Processor<C> {
@Override
public void processCommand(C command) {
// Some common stuff
// ...
// Specific process
Result result = executeCommand(command);
// More common stuff
// ...
}
protected abstract Result executeCommand(C command);
}
@Component
public AddCommandProcessor extends AbstractProcessor<AddCommand> {
@Override
protected Result executeCommand(AddCommand addCommand) {
// Execute command
// ...
return result;
}
@Override
public TypeCommand getCommandType() {
return TypeCommand.ADD_COMMAND;
}
}
Command:
public abstract class Command {
private final String uid;
private final LocalDateTime creationDate;
private final TypeCommand type;
// Constructor and getters omited ...
}
public class AddCommand extends Command {
private final Double amount;
// Constructor and getters omited ...
}
Service with chain of responsibility:
@Service
public class MyService {
private final List<Processor<? extends Command>> processors;
@Autowired
public MyService(final List<Processor<? extends Command>> processors) {
this.processors = processors;
}
public void processCommand(final Command command) {
processors.stream()
.filter(p -> command.getType() == p.getCommandType())
.findFirst()
.ifPresent(processor -> processor.processCommand(command));
}
}
Unfortunately this code does not compile. Line:
.ifPresent(processor -> processor.processCommand(command));
failed to compile message:
processCommand(capture <? extends Command>) in Processor cannot be applied to (Command)
I don't see any other way to do this on my own. Where am I going wrong?
Many thanks.
source to share
You should strip generics on Processor<? extends Command> processor
only in Processor processor
, which converts the error into a warning about raw types, which you can disable with@SuppressWarnings("rawtypes")
.ifPresent(processor -> ((Processor) processor).processCommand(command));
Another method is to create a processCommand
generic and upcast Processor<? extends Command> processor
for a generic type, for example:
public <C extends Command> void processCommand(final C command) {
processors.stream()
.filter(p -> command.getType() == p.getCommandType())
.findFirst()
.map(processor -> (Processor<C>) processor)
.ifPresent(processor -> processor.processCommand(command));
}
This will give you a warning about an uncontrolled act, which you can disable with @SuppressWarnings("unchecked")
.
Both of these methods will throw ClassCastException
if the Command
one that returns the concrete TypeCommand
is not a subclass of the type in which (the first) Processor
that returns the same TypeCommand
expects (for example, if it SubCommand
returns TypeCommand.ADD_COMMAND
but is not a subclass AddCommand
).
source to share