Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Stream and generic interface in chain of responsibility pattern

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.

like image 893
CrakBoom Avatar asked Nov 23 '25 16:11

CrakBoom


1 Answers

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).

like image 146
Raniz Avatar answered Nov 25 '25 09:11

Raniz