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.

+3


source to share


1 answer


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

).

+1


source







All Articles