In a Spring Boot application, running on Java 8, I want to implement a generic behavior to process different types of commands.
The code below illustrates the solution I had in mind.
A 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;
}
}
The 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 ...
}
The service with the 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. The line :
.ifPresent(processor -> processor.processCommand(command));
failed to compiles with the message :
processCommand(capture <? extends Command>) in Processor cannot be applied to (Command)
I don't see any other way to do it as intended. Where am I wrong ?
Thanks a lot.
You should be able to drop the generics on Processor<? extends Command> processor into just Processor processor which will transform the error into a warning about raw types which you can mute with @SuppressWarnings("rawtypes")
.ifPresent(processor -> ((Processor) processor).processCommand(command));
Another method is to make processCommand generic and upcast Processor<? extends Command> processor to the generic type, like this:
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));
}
Which will give you a warning about an unchecked cast which you can mute with @SuppressWarnings("unchecked").
Both of these methods will throw a ClassCastException if a Command that returns a specific TypeCommand isn't a subclass of the type that the (first) Processor that returns the same TypeCommand expects (e.g. if SubCommand returns TypeCommand.ADD_COMMAND but isn't a subclass of AddCommand).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With